/[LeafOK_CVS]/lbbs/src/net_server.c
ViewVC logotype

Contents of /lbbs/src/net_server.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.85 - (show annotations)
Sun Nov 16 04:40:52 2025 UTC (4 months ago) by sysadm
Branch: MAIN
Changes since 1.84: +4 -18 lines
Content type: text/x-csrc
Refine with hash_dict_inc()

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * net_server
4 * - network server with SSH support
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "bbs.h"
14 #include "bbs_main.h"
15 #include "bwf.h"
16 #include "common.h"
17 #include "database.h"
18 #include "file_loader.h"
19 #include "hash_dict.h"
20 #include "io.h"
21 #include "init.h"
22 #include "log.h"
23 #include "login.h"
24 #include "menu.h"
25 #include "net_server.h"
26 #include "section_list.h"
27 #include "section_list_loader.h"
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <pty.h>
31 #include <signal.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <utmp.h>
36 #include <arpa/inet.h>
37 #include <libssh/callbacks.h>
38 #include <libssh/libssh.h>
39 #include <libssh/server.h>
40 #include <netinet/in.h>
41 #include <sys/epoll.h>
42 #include <sys/socket.h>
43 #include <sys/syscall.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <systemd/sd-daemon.h>
47
48 enum _net_server_constant_t
49 {
50 WAIT_CHILD_PROCESS_EXIT_TIMEOUT = 5, // second
51 WAIT_CHILD_PROCESS_KILL_TIMEOUT = 1, // second
52
53 SSH_AUTH_MAX_DURATION = 60 * 1000, // milliseconds
54 };
55
56 static const char SFTP_SERVER_PATH[] = "/usr/lib/sftp-server";
57
58 /* A userdata struct for session. */
59 struct session_data_struct
60 {
61 int tries;
62 int error;
63 };
64
65 /* A userdata struct for channel. */
66 struct channel_data_struct
67 {
68 /* pid of the child process the channel will spawn. */
69 pid_t pid;
70 /* For PTY allocation */
71 socket_t pty_master;
72 socket_t pty_slave;
73 /* For communication with the child process. */
74 socket_t child_stdin;
75 socket_t child_stdout;
76 /* Only used for subsystem and exec requests. */
77 socket_t child_stderr;
78 /* Event which is used to poll the above descriptors. */
79 ssh_event event;
80 /* Terminal size struct. */
81 struct winsize *winsize;
82 };
83
84 static int auth_password(ssh_session session, const char *user,
85 const char *password, void *userdata)
86 {
87 struct session_data_struct *sdata = (struct session_data_struct *)userdata;
88 int ret;
89
90 if (strcmp(user, "guest") == 0)
91 {
92 ret = load_guest_info();
93 }
94 else
95 {
96 ret = check_user(user, password);
97 }
98
99 if (ret == 0)
100 {
101 return SSH_AUTH_SUCCESS;
102 }
103
104 if ((++(sdata->tries)) >= BBS_login_retry_times)
105 {
106 sdata->error = 1;
107 }
108
109 return SSH_AUTH_DENIED;
110 }
111
112 static int pty_request(ssh_session session, ssh_channel channel, const char *term,
113 int cols, int rows, int px, int py, void *userdata)
114 {
115 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
116 int rc;
117
118 (void)session;
119 (void)channel;
120 (void)term;
121
122 cdata->winsize->ws_row = (unsigned short int)rows;
123 cdata->winsize->ws_col = (unsigned short int)cols;
124 cdata->winsize->ws_xpixel = (unsigned short int)px;
125 cdata->winsize->ws_ypixel = (unsigned short int)py;
126
127 rc = openpty(&cdata->pty_master, &cdata->pty_slave, NULL, NULL, cdata->winsize);
128 if (rc != 0)
129 {
130 log_error("Failed to open pty\n");
131 return SSH_ERROR;
132 }
133
134 return SSH_OK;
135 }
136
137 static int pty_resize(ssh_session session, ssh_channel channel, int cols, int rows,
138 int py, int px, void *userdata)
139 {
140 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
141
142 (void)session;
143 (void)channel;
144
145 cdata->winsize->ws_row = (unsigned short int)rows;
146 cdata->winsize->ws_col = (unsigned short int)cols;
147 cdata->winsize->ws_xpixel = (unsigned short int)px;
148 cdata->winsize->ws_ypixel = (unsigned short int)py;
149
150 if (cdata->pty_master != -1)
151 {
152 return ioctl(cdata->pty_master, TIOCSWINSZ, cdata->winsize);
153 }
154
155 return SSH_ERROR;
156 }
157
158 static int exec_pty(const char *mode, const char *command, struct channel_data_struct *cdata)
159 {
160 (void)cdata;
161
162 if (command != NULL)
163 {
164 log_error("Forbid exec /bin/sh %s %s)\n", mode, command);
165 }
166
167 return SSH_OK;
168 }
169
170 static int exec_nopty(const char *command, struct channel_data_struct *cdata)
171 {
172 (void)cdata;
173
174 if (command != NULL)
175 {
176 log_error("Forbid exec /bin/sh -c %s)\n", command);
177 }
178
179 return SSH_OK;
180 }
181
182 static int exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata)
183 {
184 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
185
186 (void)session;
187 (void)channel;
188
189 if (cdata->pid > 0)
190 {
191 return SSH_ERROR;
192 }
193
194 if (cdata->pty_master != -1 && cdata->pty_slave != -1)
195 {
196 return exec_pty("-c", command, cdata);
197 }
198 return exec_nopty(command, cdata);
199 }
200
201 static int shell_request(ssh_session session, ssh_channel channel, void *userdata)
202 {
203 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
204
205 (void)session;
206 (void)channel;
207
208 if (cdata->pid > 0)
209 {
210 return SSH_ERROR;
211 }
212
213 if (cdata->pty_master != -1 && cdata->pty_slave != -1)
214 {
215 return exec_pty("-l", NULL, cdata);
216 }
217 /* Client requested a shell without a pty, let's pretend we allow that */
218 return SSH_OK;
219 }
220
221 static int subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata)
222 {
223 (void)session;
224 (void)channel;
225
226 log_error("subsystem_request(subsystem=%s)\n", subsystem);
227
228 /* subsystem requests behave similarly to exec requests. */
229 if (strcmp(subsystem, "sftp") == 0)
230 {
231 return exec_request(session, channel, SFTP_SERVER_PATH, userdata);
232 }
233 return SSH_ERROR;
234 }
235
236 static ssh_channel channel_open(ssh_session session, void *userdata)
237 {
238 (void)userdata;
239
240 if (SSH_channel != NULL)
241 {
242 return NULL;
243 }
244
245 SSH_channel = ssh_channel_new(session);
246
247 return SSH_channel;
248 }
249
250 static int fork_server(void)
251 {
252 ssh_event event;
253 long int ssh_timeout = 0;
254 int pid;
255 int i;
256 int ret;
257
258 /* Structure for storing the pty size. */
259 struct winsize wsize = {
260 .ws_row = 0,
261 .ws_col = 0,
262 .ws_xpixel = 0,
263 .ws_ypixel = 0};
264
265 /* Our struct holding information about the channel. */
266 struct channel_data_struct cdata = {
267 .pid = 0,
268 .pty_master = -1,
269 .pty_slave = -1,
270 .child_stdin = -1,
271 .child_stdout = -1,
272 .child_stderr = -1,
273 .event = NULL,
274 .winsize = &wsize};
275
276 struct session_data_struct cb_data = {
277 .tries = 0,
278 .error = 0,
279 };
280
281 struct ssh_channel_callbacks_struct channel_cb = {
282 .userdata = &cdata,
283 .channel_pty_request_function = pty_request,
284 .channel_pty_window_change_function = pty_resize,
285 .channel_shell_request_function = shell_request,
286 .channel_exec_request_function = exec_request,
287 .channel_subsystem_request_function = subsystem_request};
288
289 struct ssh_server_callbacks_struct server_cb = {
290 .userdata = &cb_data,
291 .auth_password_function = auth_password,
292 .channel_open_request_session_function = channel_open,
293 };
294
295 pid = fork();
296
297 if (pid > 0) // Parent process
298 {
299 SYS_child_process_count++;
300 log_common("Child process (%d) start\n", pid);
301 return pid;
302 }
303 else if (pid < 0) // Error
304 {
305 log_error("fork() error (%d)\n", errno);
306 return -1;
307 }
308
309 // Child process
310
311 if (close(socket_server[0]) == -1 || close(socket_server[1]) == -1)
312 {
313 log_error("Close server socket failed\n");
314 }
315
316 SSH_session = ssh_new();
317
318 if (SSH_v2)
319 {
320 if (ssh_bind_accept_fd(sshbind, SSH_session, socket_client) != SSH_OK)
321 {
322 log_error("ssh_bind_accept_fd() error: %s\n", ssh_get_error(SSH_session));
323 goto cleanup;
324 }
325
326 ssh_bind_free(sshbind);
327
328 ssh_timeout = 60; // second
329 if (ssh_options_set(SSH_session, SSH_OPTIONS_TIMEOUT, &ssh_timeout) < 0)
330 {
331 log_error("Error setting SSH options: %s\n", ssh_get_error(SSH_session));
332 goto cleanup;
333 }
334
335 ssh_set_auth_methods(SSH_session, SSH_AUTH_METHOD_PASSWORD);
336
337 ssh_callbacks_init(&server_cb);
338 ssh_callbacks_init(&channel_cb);
339
340 ssh_set_server_callbacks(SSH_session, &server_cb);
341
342 if (ssh_handle_key_exchange(SSH_session))
343 {
344 log_error("ssh_handle_key_exchange() error: %s\n", ssh_get_error(SSH_session));
345 goto cleanup;
346 }
347
348 event = ssh_event_new();
349 ssh_event_add_session(event, SSH_session);
350
351 for (i = 0; i < SSH_AUTH_MAX_DURATION && !SYS_server_exit && !cb_data.error && SSH_channel == NULL; i += 100)
352 {
353 ret = ssh_event_dopoll(event, 100); // 0.1 second
354 if (ret == SSH_ERROR)
355 {
356 #ifdef _DEBUG
357 log_error("ssh_event_dopoll() error: %s\n", ssh_get_error(SSH_session));
358 #endif
359 goto cleanup;
360 }
361 }
362
363 if (cb_data.error)
364 {
365 log_error("SSH auth error, tried %d times\n", cb_data.tries);
366 goto cleanup;
367 }
368
369 ssh_set_channel_callbacks(SSH_channel, &channel_cb);
370
371 do
372 {
373 ret = ssh_event_dopoll(event, 100); // 0.1 second
374 if (ret == SSH_ERROR)
375 {
376 ssh_channel_close(SSH_channel);
377 }
378
379 if (ret == SSH_AGAIN) // loop until SSH connection is fully established
380 {
381 /* Executed only once, once the child process starts. */
382 cdata.event = event;
383 break;
384 }
385 } while (ssh_channel_is_open(SSH_channel));
386
387 ssh_timeout = 0;
388 if (ssh_options_set(SSH_session, SSH_OPTIONS_TIMEOUT, &ssh_timeout) < 0)
389 {
390 log_error("Error setting SSH options: %s\n", ssh_get_error(SSH_session));
391 goto cleanup;
392 }
393 }
394
395 // Redirect Input
396 if (dup2(socket_client, STDIN_FILENO) == -1)
397 {
398 log_error("Redirect stdin to client socket failed\n");
399 goto cleanup;
400 }
401
402 // Redirect Output
403 if (dup2(socket_client, STDOUT_FILENO) == -1)
404 {
405 log_error("Redirect stdout to client socket failed\n");
406 goto cleanup;
407 }
408
409 SYS_child_process_count = 0;
410
411 bbs_main();
412
413 cleanup:
414 // Child process exit
415 SYS_server_exit = 1;
416
417 if (SSH_v2)
418 {
419 close(cdata.pty_master);
420 close(cdata.child_stdin);
421 close(cdata.child_stdout);
422 close(cdata.child_stderr);
423
424 ssh_channel_free(SSH_channel);
425 ssh_disconnect(SSH_session);
426 }
427 else if (close(socket_client) == -1)
428 {
429 log_error("Close client socket failed\n");
430 }
431
432 ssh_free(SSH_session);
433 ssh_finalize();
434
435 // Close Input and Output for client
436 close(STDIN_FILENO);
437 close(STDOUT_FILENO);
438
439 log_common("Process exit normally\n");
440 log_end();
441
442 _exit(0);
443
444 return 0;
445 }
446
447 int net_server(const char *hostaddr, in_port_t port[])
448 {
449 HASH_DICT *hash_dict_pid_sockaddr = NULL;
450 HASH_DICT *hash_dict_sockaddr_count = NULL;
451
452 unsigned int addrlen;
453 int ret;
454 int flags[2];
455 struct sockaddr_in sin;
456 struct epoll_event ev, events[MAX_EVENTS];
457 int nfds, epollfd;
458 siginfo_t siginfo;
459 int notify_child_exit = 0;
460 time_t tm_notify_child_exit = time(NULL);
461 int sd_notify_stopping = 0;
462 MENU_SET bbs_menu_new;
463 MENU_SET top10_menu_new;
464 int i, j;
465 pid_t pid;
466 int ssh_log_level = SSH_LOG_NOLOG;
467
468 ssh_init();
469
470 sshbind = ssh_bind_new();
471
472 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 ||
473 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 ||
474 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_KEYFILE) < 0 ||
475 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "ssh-rsa,rsa-sha2-512,rsa-sha2-256") < 0 ||
476 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0)
477 {
478 log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind));
479 ssh_bind_free(sshbind);
480 return -1;
481 }
482
483 epollfd = epoll_create1(0);
484 if (epollfd < 0)
485 {
486 log_error("epoll_create1() error (%d)\n", errno);
487 return -1;
488 }
489
490 // Server socket
491 for (i = 0; i < 2; i++)
492 {
493 socket_server[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
494
495 if (socket_server[i] < 0)
496 {
497 log_error("Create socket_server error (%d)\n", errno);
498 return -1;
499 }
500
501 sin.sin_family = AF_INET;
502 sin.sin_addr.s_addr = (hostaddr[0] != '\0' ? inet_addr(hostaddr) : INADDR_ANY);
503 sin.sin_port = htons(port[i]);
504
505 // Reuse address and port
506 flags[i] = 1;
507 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEADDR, &flags[i], sizeof(flags[i])) < 0)
508 {
509 log_error("setsockopt SO_REUSEADDR error (%d)\n", errno);
510 }
511 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEPORT, &flags[i], sizeof(flags[i])) < 0)
512 {
513 log_error("setsockopt SO_REUSEPORT error (%d)\n", errno);
514 }
515
516 if (bind(socket_server[i], (struct sockaddr *)&sin, sizeof(sin)) < 0)
517 {
518 log_error("Bind address %s:%u error (%d)\n",
519 inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), errno);
520 return -1;
521 }
522
523 if (listen(socket_server[i], 10) < 0)
524 {
525 log_error("Telnet socket listen error (%d)\n", errno);
526 return -1;
527 }
528
529 log_common("Listening at %s:%u\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
530
531 ev.events = EPOLLIN;
532 ev.data.fd = socket_server[i];
533 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, socket_server[i], &ev) == -1)
534 {
535 log_error("epoll_ctl(socket_server[%d]) error (%d)\n", i, errno);
536 if (close(epollfd) < 0)
537 {
538 log_error("close(epoll) error (%d)\n");
539 }
540 return -1;
541 }
542
543 flags[i] = fcntl(socket_server[i], F_GETFL, 0);
544 fcntl(socket_server[i], F_SETFL, flags[i] | O_NONBLOCK);
545 }
546
547 hash_dict_pid_sockaddr = hash_dict_create(MAX_CLIENT_LIMIT);
548 if (hash_dict_pid_sockaddr == NULL)
549 {
550 log_error("hash_dict_create(hash_dict_pid_sockaddr) error\n");
551 return -1;
552 }
553 hash_dict_sockaddr_count = hash_dict_create(MAX_CLIENT_LIMIT);
554 if (hash_dict_sockaddr_count == NULL)
555 {
556 log_error("hash_dict_create(hash_dict_sockaddr_count) error\n");
557 return -1;
558 }
559
560 // Startup complete
561 sd_notifyf(0, "READY=1\n"
562 "STATUS=Listening at %s:%d (Telnet) and %s:%d (SSH2)\n"
563 "MAINPID=%d",
564 hostaddr, port[0], hostaddr, port[1], getpid());
565
566 while (!SYS_server_exit || SYS_child_process_count > 0)
567 {
568 if (SYS_server_exit && !sd_notify_stopping)
569 {
570 sd_notify(0, "STOPPING=1");
571 sd_notify_stopping = 1;
572 }
573
574 while ((SYS_child_exit || SYS_server_exit) && SYS_child_process_count > 0)
575 {
576 SYS_child_exit = 0;
577
578 siginfo.si_pid = 0;
579 ret = waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG);
580 if (ret == 0 && siginfo.si_pid > 0)
581 {
582 SYS_child_exit = 1; // Retry waitid
583
584 SYS_child_process_count--;
585 log_common("Child process (%d) exited\n", siginfo.si_pid);
586
587 if (siginfo.si_pid != section_list_loader_pid)
588 {
589 j = 0;
590 ret = hash_dict_get(hash_dict_pid_sockaddr, (uint64_t)siginfo.si_pid, (int64_t *)&j);
591 if (ret < 0)
592 {
593 log_error("hash_dict_get(hash_dict_pid_sockaddr, %d) error\n", siginfo.si_pid);
594 }
595 else
596 {
597 ret = hash_dict_inc(hash_dict_sockaddr_count, (uint64_t)j, -1);
598 if (ret < 0)
599 {
600 log_error("hash_dict_inc(hash_dict_sockaddr_count, %d, -1) error\n", j);
601 }
602
603 ret = hash_dict_del(hash_dict_pid_sockaddr, (uint64_t)siginfo.si_pid);
604 if (ret < 0)
605 {
606 log_error("hash_dict_del(hash_dict_pid_sockaddr, %d) error\n", siginfo.si_pid);
607 }
608 }
609 }
610 }
611 else if (ret == 0)
612 {
613 break;
614 }
615 else if (ret < 0)
616 {
617 log_error("Error in waitid: %d\n", errno);
618 break;
619 }
620 }
621
622 if (SYS_server_exit && !SYS_child_exit && SYS_child_process_count > 0)
623 {
624 if (notify_child_exit == 0)
625 {
626 sd_notifyf(0, "STATUS=Notify %d child process to exit", SYS_child_process_count);
627 log_common("Notify %d child process to exit\n", SYS_child_process_count);
628
629 if (kill(-getpid(), SIGTERM) < 0)
630 {
631 log_error("Send SIGTERM signal failed (%d)\n", errno);
632 }
633
634 notify_child_exit = 1;
635 tm_notify_child_exit = time(NULL);
636 }
637 else if (notify_child_exit == 1 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_EXIT_TIMEOUT)
638 {
639 sd_notifyf(0, "STATUS=Kill %d child process", SYS_child_process_count);
640
641 if (kill(-getpid(), SIGKILL) < 0)
642 {
643 log_error("Send SIGKILL signal failed (%d)\n", errno);
644 }
645
646 notify_child_exit = 2;
647 tm_notify_child_exit = time(NULL);
648 }
649 else if (notify_child_exit == 2 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_KILL_TIMEOUT)
650 {
651 log_error("Main process prepare to exit without waiting for %d child process any longer\n", SYS_child_process_count);
652 SYS_child_process_count = 0;
653 }
654 }
655
656 if (SYS_conf_reload && !SYS_server_exit)
657 {
658 SYS_conf_reload = 0;
659 sd_notify(0, "RELOADING=1");
660
661 // Restart log
662 if (log_restart() < 0)
663 {
664 log_error("Restart logging failed\n");
665 }
666
667 // Reload configuration
668 if (load_conf(CONF_BBSD) < 0)
669 {
670 log_error("Reload conf failed\n");
671 }
672
673 // Reload BWF config
674 if (bwf_load(CONF_BWF) < 0)
675 {
676 log_error("Reload BWF conf failed\n");
677 }
678
679 if (load_menu(&bbs_menu_new, CONF_MENU) < 0)
680 {
681 unload_menu(&bbs_menu_new);
682 log_error("Reload bbs menu failed\n");
683 }
684 else
685 {
686 unload_menu(&bbs_menu);
687 memcpy(&bbs_menu, &bbs_menu_new, sizeof(bbs_menu_new));
688 log_common("Reload bbs menu successfully\n");
689 }
690
691 if (load_menu(&top10_menu_new, CONF_TOP10_MENU) < 0)
692 {
693 unload_menu(&top10_menu_new);
694 log_error("Reload top10 menu failed\n");
695 }
696 else
697 {
698 unload_menu(&top10_menu);
699 top10_menu_new.allow_exit = 1;
700 memcpy(&top10_menu, &top10_menu_new, sizeof(top10_menu_new));
701 log_common("Reload top10 menu successfully\n");
702 }
703
704 for (int i = 0; i < data_files_load_startup_count; i++)
705 {
706 if (load_file(data_files_load_startup[i]) < 0)
707 {
708 log_error("load_file(%s) error\n", data_files_load_startup[i]);
709 }
710 }
711 log_common("Reload data files successfully\n");
712
713 // Load section config and gen_ex
714 if (load_section_config_from_db(1) < 0)
715 {
716 log_error("load_section_config_from_db(1) error\n");
717 }
718 else
719 {
720 log_common("Reload section config and gen_ex successfully\n");
721 }
722
723 // Notify child processes to reload configuration
724 if (kill(-getpid(), SIGUSR1) < 0)
725 {
726 log_error("Send SIGUSR1 signal failed (%d)\n", errno);
727 }
728
729 sd_notify(0, "READY=1");
730 }
731
732 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
733
734 if (nfds < 0)
735 {
736 if (errno != EINTR)
737 {
738 log_error("epoll_wait() error (%d)\n", errno);
739 break;
740 }
741 continue;
742 }
743
744 // Stop accept new connection on exit
745 if (SYS_server_exit)
746 {
747 continue;
748 }
749
750 for (int i = 0; i < nfds; i++)
751 {
752 if (events[i].data.fd == socket_server[0] || events[i].data.fd == socket_server[1])
753 {
754 SSH_v2 = (events[i].data.fd == socket_server[1] ? 1 : 0);
755
756 while (!SYS_server_exit) // Accept all incoming connections until error
757 {
758 addrlen = sizeof(sin);
759 socket_client = accept(socket_server[SSH_v2], (struct sockaddr *)&sin, &addrlen);
760 if (socket_client < 0)
761 {
762 if (errno == EAGAIN || errno == EWOULDBLOCK)
763 {
764 break;
765 }
766 else if (errno == EINTR)
767 {
768 continue;
769 }
770 else
771 {
772 log_error("accept(socket_server) error (%d)\n", errno);
773 break;
774 }
775 }
776
777 strncpy(hostaddr_client, inet_ntoa(sin.sin_addr), sizeof(hostaddr_client) - 1);
778 hostaddr_client[sizeof(hostaddr_client) - 1] = '\0';
779
780 port_client = ntohs(sin.sin_port);
781
782 log_common("Accept %s connection from %s:%d\n", (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client);
783
784 if (SYS_child_process_count - 1 < BBS_max_client)
785 {
786 j = 0;
787 ret = hash_dict_get(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, (int64_t *)&j);
788 if (ret < 0)
789 {
790 log_error("hash_dict_get(hash_dict_sockaddr_count, %s) error\n", hostaddr_client);
791 }
792
793 if (j < BBS_max_client_per_ip)
794 {
795 if ((pid = fork_server()) < 0)
796 {
797 log_error("fork_server() error\n");
798 }
799 else if (pid > 0)
800 {
801 ret = hash_dict_set(hash_dict_pid_sockaddr, (uint64_t)pid, sin.sin_addr.s_addr);
802 if (ret < 0)
803 {
804 log_error("hash_dict_set(hash_dict_pid_sockaddr, %d, %s) error\n", pid, hostaddr_client);
805 }
806
807 ret = hash_dict_inc(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, 1);
808 if (ret < 0)
809 {
810 log_error("hash_dict_inc(hash_dict_sockaddr_count, %s, %d) error\n", hostaddr_client, 1);
811 }
812 }
813 }
814 else
815 {
816 log_error("Rejected client connection from %s over limit per IP (%d)\n", hostaddr_client, BBS_max_client_per_ip);
817 }
818 }
819 else
820 {
821 log_error("Rejected client connection over limit (%d)\n", SYS_child_process_count - 1);
822 }
823
824 if (close(socket_client) == -1)
825 {
826 log_error("close(socket_lient) error (%d)\n", errno);
827 }
828 }
829 }
830 }
831 }
832
833 if (close(epollfd) < 0)
834 {
835 log_error("close(epoll) error (%d)\n");
836 }
837
838 for (i = 0; i < 2; i++)
839 {
840 fcntl(socket_server[i], F_SETFL, flags[i]);
841
842 if (close(socket_server[i]) == -1)
843 {
844 log_error("Close server socket failed\n");
845 }
846 }
847
848 hash_dict_destroy(hash_dict_pid_sockaddr);
849 hash_dict_destroy(hash_dict_sockaddr_count);
850
851 ssh_bind_free(sshbind);
852 ssh_finalize();
853
854 return 0;
855 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1