/[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.107 - (show annotations)
Wed Dec 17 04:22:40 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.106: +2 -2 lines
Content type: text/x-csrc
Fix bug

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/ioctl.h>
42 #include <sys/socket.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46
47 #ifdef HAVE_SYS_EPOLL_H
48 #include <sys/epoll.h>
49 #else
50 #include <poll.h>
51 #endif
52
53 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
54 #include <systemd/sd-daemon.h>
55 #endif
56
57 enum _net_server_constant_t
58 {
59 WAIT_CHILD_PROCESS_EXIT_TIMEOUT = 5, // second
60 WAIT_CHILD_PROCESS_KILL_TIMEOUT = 1, // second
61
62 SSH_AUTH_MAX_DURATION = 60 * 1000, // milliseconds
63 };
64
65 /* A userdata struct for session. */
66 struct session_data_struct
67 {
68 int tries;
69 int error;
70 };
71
72 /* A userdata struct for channel. */
73 struct channel_data_struct
74 {
75 /* pid of the child process the channel will spawn. */
76 pid_t pid;
77 /* For PTY allocation */
78 socket_t pty_master;
79 socket_t pty_slave;
80 /* For communication with the child process. */
81 socket_t child_stdin;
82 socket_t child_stdout;
83 /* Only used for subsystem and exec requests. */
84 socket_t child_stderr;
85 /* Event which is used to poll the above descriptors. */
86 ssh_event event;
87 /* Terminal size struct. */
88 struct winsize *winsize;
89 };
90
91 static int socket_server[2];
92 static int socket_client;
93
94 #ifdef HAVE_SYS_EPOLL_H
95 static int epollfd_server = -1;
96 #endif
97
98 static ssh_bind sshbind;
99
100 static HASH_DICT *hash_dict_pid_sockaddr = NULL;
101 static HASH_DICT *hash_dict_sockaddr_count = NULL;
102
103 static const char SFTP_SERVER_PATH[] = "/usr/lib/sftp-server";
104
105 static int auth_password(ssh_session session, const char *user,
106 const char *password, void *userdata)
107 {
108 struct session_data_struct *sdata = (struct session_data_struct *)userdata;
109 int ret;
110
111 if (strcmp(user, "guest") == 0)
112 {
113 ret = load_guest_info();
114 }
115 else
116 {
117 ret = check_user(user, password);
118 if (ret == 2) // Enforce update user agreement
119 {
120 BBS_update_eula = 1;
121 ret = 0;
122 }
123 }
124
125 if (ret == 0)
126 {
127 return SSH_AUTH_SUCCESS;
128 }
129
130 if ((++(sdata->tries)) >= BBS_login_retry_times)
131 {
132 sdata->error = 1;
133 }
134
135 return SSH_AUTH_DENIED;
136 }
137
138 static int pty_request(ssh_session session, ssh_channel channel, const char *term,
139 int cols, int rows, int px, int py, void *userdata)
140 {
141 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
142 int rc;
143
144 (void)session;
145 (void)channel;
146 (void)term;
147
148 cdata->winsize->ws_row = (unsigned short int)rows;
149 cdata->winsize->ws_col = (unsigned short int)cols;
150 cdata->winsize->ws_xpixel = (unsigned short int)px;
151 cdata->winsize->ws_ypixel = (unsigned short int)py;
152
153 rc = openpty(&cdata->pty_master, &cdata->pty_slave, NULL, NULL, cdata->winsize);
154 if (rc != 0)
155 {
156 log_error("Failed to open pty\n");
157 return SSH_ERROR;
158 }
159
160 return SSH_OK;
161 }
162
163 static int pty_resize(ssh_session session, ssh_channel channel, int cols, int rows,
164 int py, int px, void *userdata)
165 {
166 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
167
168 (void)session;
169 (void)channel;
170
171 cdata->winsize->ws_row = (unsigned short int)rows;
172 cdata->winsize->ws_col = (unsigned short int)cols;
173 cdata->winsize->ws_xpixel = (unsigned short int)px;
174 cdata->winsize->ws_ypixel = (unsigned short int)py;
175
176 if (cdata->pty_master != -1)
177 {
178 return ioctl(cdata->pty_master, TIOCSWINSZ, cdata->winsize);
179 }
180
181 return SSH_ERROR;
182 }
183
184 static int exec_pty(const char *mode, const char *command, struct channel_data_struct *cdata)
185 {
186 (void)cdata;
187
188 if (command != NULL)
189 {
190 log_error("Forbid exec /bin/sh %s %s)\n", mode, command);
191 }
192
193 return SSH_OK;
194 }
195
196 static int exec_nopty(const char *command, struct channel_data_struct *cdata)
197 {
198 (void)cdata;
199
200 if (command != NULL)
201 {
202 log_error("Forbid exec /bin/sh -c %s)\n", command);
203 }
204
205 return SSH_OK;
206 }
207
208 static int exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata)
209 {
210 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
211
212 (void)session;
213 (void)channel;
214
215 if (cdata->pid > 0)
216 {
217 return SSH_ERROR;
218 }
219
220 if (cdata->pty_master != -1 && cdata->pty_slave != -1)
221 {
222 return exec_pty("-c", command, cdata);
223 }
224 return exec_nopty(command, cdata);
225 }
226
227 static int shell_request(ssh_session session, ssh_channel channel, void *userdata)
228 {
229 struct channel_data_struct *cdata = (struct channel_data_struct *)userdata;
230
231 (void)session;
232 (void)channel;
233
234 if (cdata->pid > 0)
235 {
236 return SSH_ERROR;
237 }
238
239 if (cdata->pty_master != -1 && cdata->pty_slave != -1)
240 {
241 return exec_pty("-l", NULL, cdata);
242 }
243 /* Client requested a shell without a pty, let's pretend we allow that */
244 return SSH_OK;
245 }
246
247 static int subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata)
248 {
249 (void)session;
250 (void)channel;
251
252 log_error("subsystem_request(subsystem=%s)\n", subsystem);
253
254 /* subsystem requests behave similarly to exec requests. */
255 if (strcmp(subsystem, "sftp") == 0)
256 {
257 return exec_request(session, channel, SFTP_SERVER_PATH, userdata);
258 }
259 return SSH_ERROR;
260 }
261
262 static ssh_channel channel_open(ssh_session session, void *userdata)
263 {
264 (void)userdata;
265
266 if (SSH_channel != NULL)
267 {
268 return NULL;
269 }
270
271 SSH_channel = ssh_channel_new(session);
272
273 return SSH_channel;
274 }
275
276 static int fork_server(void)
277 {
278 ssh_event event;
279 long int ssh_timeout = 0;
280 int pid;
281 int i;
282 int ret;
283
284 /* Structure for storing the pty size. */
285 struct winsize wsize = {
286 .ws_row = 0,
287 .ws_col = 0,
288 .ws_xpixel = 0,
289 .ws_ypixel = 0};
290
291 /* Our struct holding information about the channel. */
292 struct channel_data_struct cdata = {
293 .pid = 0,
294 .pty_master = -1,
295 .pty_slave = -1,
296 .child_stdin = -1,
297 .child_stdout = -1,
298 .child_stderr = -1,
299 .event = NULL,
300 .winsize = &wsize};
301
302 struct session_data_struct cb_data = {
303 .tries = 0,
304 .error = 0,
305 };
306
307 struct ssh_channel_callbacks_struct channel_cb = {
308 .userdata = &cdata,
309 .channel_pty_request_function = pty_request,
310 .channel_pty_window_change_function = pty_resize,
311 .channel_shell_request_function = shell_request,
312 .channel_exec_request_function = exec_request,
313 .channel_subsystem_request_function = subsystem_request};
314
315 struct ssh_server_callbacks_struct server_cb = {
316 .userdata = &cb_data,
317 .auth_password_function = auth_password,
318 .channel_open_request_session_function = channel_open,
319 };
320
321 pid = fork();
322
323 if (pid > 0) // Parent process
324 {
325 SYS_child_process_count++;
326 log_common("Child process (%d) start\n", pid);
327 return pid;
328 }
329 else if (pid < 0) // Error
330 {
331 log_error("fork() error (%d)\n", errno);
332 return -1;
333 }
334
335 // Child process
336 #ifdef HAVE_SYS_EPOLL_H
337 if (close(epollfd_server) < 0)
338 {
339 log_error("close(epollfd_server) error (%d)\n");
340 }
341 #endif
342
343 for (i = 0; i < 2; i++)
344 {
345 if (close(socket_server[i]) == -1)
346 {
347 log_error("Close server socket failed\n");
348 }
349 }
350
351 hash_dict_destroy(hash_dict_pid_sockaddr);
352 hash_dict_destroy(hash_dict_sockaddr_count);
353
354 SSH_session = ssh_new();
355
356 if (SSH_v2)
357 {
358 if (ssh_bind_accept_fd(sshbind, SSH_session, socket_client) != SSH_OK)
359 {
360 log_error("ssh_bind_accept_fd() error: %s\n", ssh_get_error(SSH_session));
361 goto cleanup;
362 }
363
364 ssh_bind_free(sshbind);
365
366 ssh_timeout = 60; // second
367 if (ssh_options_set(SSH_session, SSH_OPTIONS_TIMEOUT, &ssh_timeout) < 0)
368 {
369 log_error("Error setting SSH options: %s\n", ssh_get_error(SSH_session));
370 goto cleanup;
371 }
372
373 ssh_set_auth_methods(SSH_session, SSH_AUTH_METHOD_PASSWORD);
374
375 ssh_callbacks_init(&server_cb);
376 ssh_callbacks_init(&channel_cb);
377
378 ssh_set_server_callbacks(SSH_session, &server_cb);
379
380 if (ssh_handle_key_exchange(SSH_session))
381 {
382 log_error("ssh_handle_key_exchange() error: %s\n", ssh_get_error(SSH_session));
383 goto cleanup;
384 }
385
386 event = ssh_event_new();
387 ssh_event_add_session(event, SSH_session);
388
389 for (i = 0; i < SSH_AUTH_MAX_DURATION && !SYS_server_exit && !cb_data.error && SSH_channel == NULL; i += 100)
390 {
391 ret = ssh_event_dopoll(event, 100); // 0.1 second
392 if (ret == SSH_ERROR)
393 {
394 #ifdef _DEBUG
395 log_error("ssh_event_dopoll() error: %s\n", ssh_get_error(SSH_session));
396 #endif
397 goto cleanup;
398 }
399 }
400
401 if (cb_data.error)
402 {
403 log_error("SSH auth error, tried %d times\n", cb_data.tries);
404 goto cleanup;
405 }
406
407 ssh_set_channel_callbacks(SSH_channel, &channel_cb);
408
409 do
410 {
411 ret = ssh_event_dopoll(event, 100); // 0.1 second
412 if (ret == SSH_ERROR)
413 {
414 ssh_channel_close(SSH_channel);
415 }
416
417 if (ret == SSH_AGAIN) // loop until SSH connection is fully established
418 {
419 /* Executed only once, once the child process starts. */
420 cdata.event = event;
421 break;
422 }
423 } while (ssh_channel_is_open(SSH_channel));
424
425 ssh_timeout = 0;
426 if (ssh_options_set(SSH_session, SSH_OPTIONS_TIMEOUT, &ssh_timeout) < 0)
427 {
428 log_error("Error setting SSH options: %s\n", ssh_get_error(SSH_session));
429 goto cleanup;
430 }
431
432 ssh_set_blocking(SSH_session, 0);
433 }
434
435 // Redirect Input
436 if (dup2(socket_client, STDIN_FILENO) == -1)
437 {
438 log_error("Redirect stdin to client socket failed\n");
439 goto cleanup;
440 }
441
442 // Redirect Output
443 if (dup2(socket_client, STDOUT_FILENO) == -1)
444 {
445 log_error("Redirect stdout to client socket failed\n");
446 goto cleanup;
447 }
448
449 if (io_init() < 0)
450 {
451 log_error("io_init() error\n");
452 goto cleanup;
453 }
454
455 SYS_child_process_count = 0;
456
457 // BWF compile
458 if (bwf_compile() < 0)
459 {
460 log_error("bwf_compile() error\n");
461 goto cleanup;
462 }
463
464 bbs_main();
465
466 cleanup:
467 // Child process exit
468 SYS_server_exit = 1;
469
470 if (SSH_v2)
471 {
472 if (cdata.pty_master != -1)
473 {
474 close(cdata.pty_master);
475 }
476 if (cdata.child_stdin != -1)
477 {
478 close(cdata.child_stdin);
479 }
480 if (cdata.child_stdout != -1)
481 {
482 close(cdata.child_stdout);
483 }
484 if (cdata.child_stderr != -1)
485 {
486 close(cdata.child_stderr);
487 }
488
489 ssh_channel_free(SSH_channel);
490 ssh_disconnect(SSH_session);
491 }
492 else if (close(socket_client) == -1)
493 {
494 log_error("Close client socket failed\n");
495 }
496
497 ssh_free(SSH_session);
498 ssh_finalize();
499
500 // BWF cleanup
501 bwf_cleanup();
502
503 // Close Input and Output for client
504 io_cleanup();
505 close(STDIN_FILENO);
506 close(STDOUT_FILENO);
507
508 log_common("Process exit normally\n");
509 log_end();
510
511 _exit(0);
512
513 return 0;
514 }
515
516 int net_server(const char *hostaddr, in_port_t port[])
517 {
518 struct stat file_stat;
519 unsigned int addrlen;
520 int ret;
521 int flags_server[2];
522 struct sockaddr_in sin;
523
524 #ifdef HAVE_SYS_EPOLL_H
525 struct epoll_event ev, events[MAX_EVENTS];
526 #else
527 struct pollfd pfds[2];
528 #endif
529
530 int nfds;
531 int notify_child_exit = 0;
532 time_t tm_notify_child_exit = time(NULL);
533 int i, j;
534 pid_t pid;
535 int ssh_key_valid = 0;
536 int ssh_log_level = SSH_LOG_NOLOG;
537
538 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
539 int sd_notify_stopping = 0;
540 #endif
541
542 ssh_init();
543
544 sshbind = ssh_bind_new();
545
546 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_RSA_KEY_FILE) < 0)
547 {
548 log_error("Error loading SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE);
549 }
550 else
551 {
552 ssh_key_valid = 1;
553 }
554 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ED25519_KEY_FILE) < 0)
555 {
556 log_error("Error loading SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE);
557 }
558 else
559 {
560 ssh_key_valid = 1;
561 }
562 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ECDSA_KEY_FILE) < 0)
563 {
564 log_error("Error loading SSH ECDSA key: %s\n", SSH_HOST_ECDSA_KEY_FILE);
565 }
566 else
567 {
568 ssh_key_valid = 1;
569 }
570
571 if (!ssh_key_valid)
572 {
573 log_error("Error: no valid SSH host key\n");
574 ssh_bind_free(sshbind);
575 return -1;
576 }
577
578 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 ||
579 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 ||
580 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "+ssh-rsa") < 0 ||
581 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0)
582 {
583 log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind));
584 ssh_bind_free(sshbind);
585 return -1;
586 }
587
588 #ifdef HAVE_SYS_EPOLL_H
589 epollfd_server = epoll_create1(0);
590 if (epollfd_server == -1)
591 {
592 log_error("epoll_create1() error (%d)\n", errno);
593 return -1;
594 }
595 #endif
596
597 // Server socket
598 for (i = 0; i < 2; i++)
599 {
600 socket_server[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
601
602 if (socket_server[i] < 0)
603 {
604 log_error("Create socket_server error (%d)\n", errno);
605 return -1;
606 }
607
608 sin.sin_family = AF_INET;
609 sin.sin_addr.s_addr = (hostaddr[0] != '\0' ? inet_addr(hostaddr) : INADDR_ANY);
610 sin.sin_port = htons(port[i]);
611
612 // Reuse address and port
613 flags_server[i] = 1;
614 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEADDR, &flags_server[i], sizeof(flags_server[i])) < 0)
615 {
616 log_error("setsockopt SO_REUSEADDR error (%d)\n", errno);
617 }
618 #if defined(SO_REUSEPORT)
619 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEPORT, &flags_server[i], sizeof(flags_server[i])) < 0)
620 {
621 log_error("setsockopt SO_REUSEPORT error (%d)\n", errno);
622 }
623 #endif
624
625 if (bind(socket_server[i], (struct sockaddr *)&sin, sizeof(sin)) < 0)
626 {
627 log_error("Bind address %s:%u error (%d)\n",
628 inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), errno);
629 return -1;
630 }
631
632 if (listen(socket_server[i], 10) < 0)
633 {
634 log_error("Telnet socket listen error (%d)\n", errno);
635 return -1;
636 }
637
638 log_common("Listening at %s:%u\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
639
640 #ifdef HAVE_SYS_EPOLL_H
641 ev.events = EPOLLIN;
642 ev.data.fd = socket_server[i];
643 if (epoll_ctl(epollfd_server, EPOLL_CTL_ADD, socket_server[i], &ev) == -1)
644 {
645 log_error("epoll_ctl(socket_server[%d]) error (%d)\n", i, errno);
646 if (close(epollfd_server) < 0)
647 {
648 log_error("close(epoll) error (%d)\n");
649 }
650 return -1;
651 }
652 #endif
653
654 flags_server[i] = fcntl(socket_server[i], F_GETFL, 0);
655 fcntl(socket_server[i], F_SETFL, flags_server[i] | O_NONBLOCK);
656 }
657
658 ssh_bind_set_blocking(sshbind, 0);
659
660 hash_dict_pid_sockaddr = hash_dict_create(MAX_CLIENT_LIMIT);
661 if (hash_dict_pid_sockaddr == NULL)
662 {
663 log_error("hash_dict_create(hash_dict_pid_sockaddr) error\n");
664 return -1;
665 }
666 hash_dict_sockaddr_count = hash_dict_create(MAX_CLIENT_LIMIT);
667 if (hash_dict_sockaddr_count == NULL)
668 {
669 log_error("hash_dict_create(hash_dict_sockaddr_count) error\n");
670 return -1;
671 }
672
673 // Startup complete
674 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
675 sd_notifyf(0, "READY=1\n"
676 "STATUS=Listening at %s:%d (Telnet) and %s:%d (SSH2)\n"
677 "MAINPID=%d",
678 hostaddr, port[0], hostaddr, port[1], getpid());
679 #endif
680
681 while (!SYS_server_exit || SYS_child_process_count > 0)
682 {
683 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
684 if (SYS_server_exit && !sd_notify_stopping)
685 {
686 sd_notify(0, "STOPPING=1");
687 sd_notify_stopping = 1;
688 }
689 #endif
690
691 while ((SYS_child_exit || SYS_server_exit) && SYS_child_process_count > 0)
692 {
693 SYS_child_exit = 0;
694
695 pid = waitpid(-1, &ret, WNOHANG);
696 if (pid > 0)
697 {
698 SYS_child_exit = 1; // Retry waitid
699 SYS_child_process_count--;
700
701 if (WIFEXITED(ret))
702 {
703 log_common("Child process (%d) exited, status=%d\n", pid, WEXITSTATUS(ret));
704 }
705 else if (WIFSIGNALED(ret))
706 {
707 log_common("Child process (%d) is killed, status=%d\n", pid, WTERMSIG(ret));
708 }
709 else
710 {
711 log_common("Child process (%d) exited abnormally, status=%d\n", pid, ret);
712 }
713
714 if (pid != section_list_loader_pid)
715 {
716 j = 0;
717 ret = hash_dict_get(hash_dict_pid_sockaddr, (uint64_t)pid, (int64_t *)&j);
718 if (ret < 0)
719 {
720 log_error("hash_dict_get(hash_dict_pid_sockaddr, %d) error\n", pid);
721 }
722 else
723 {
724 ret = hash_dict_inc(hash_dict_sockaddr_count, (in_addr_t)j, -1);
725 if (ret <= 0)
726 {
727 log_error("hash_dict_inc(hash_dict_sockaddr_count, %d, -1) error: %d\n", (in_addr_t)j, ret);
728 }
729
730 ret = hash_dict_del(hash_dict_pid_sockaddr, (uint64_t)pid);
731 if (ret < 0)
732 {
733 log_error("hash_dict_del(hash_dict_pid_sockaddr, %d) error\n", pid);
734 }
735 }
736 }
737 }
738 else if (pid == 0)
739 {
740 break;
741 }
742 else if (pid < 0)
743 {
744 log_error("Error in waitpid(): %d\n", errno);
745 break;
746 }
747 }
748
749 if (SYS_server_exit && !SYS_child_exit && SYS_child_process_count > 0)
750 {
751 if (notify_child_exit == 0)
752 {
753 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
754 sd_notifyf(0, "STATUS=Notify %d child process to exit", SYS_child_process_count);
755 log_common("Notify %d child process to exit\n", SYS_child_process_count);
756 #endif
757
758 if (kill(0, SIGTERM) < 0)
759 {
760 log_error("Send SIGTERM signal failed (%d)\n", errno);
761 }
762
763 notify_child_exit = 1;
764 tm_notify_child_exit = time(NULL);
765 }
766 else if (notify_child_exit == 1 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_EXIT_TIMEOUT)
767 {
768 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
769 sd_notifyf(0, "STATUS=Kill %d child process", SYS_child_process_count);
770 #endif
771
772 if (kill(0, SIGKILL) < 0)
773 {
774 log_error("Send SIGKILL signal failed (%d)\n", errno);
775 }
776
777 notify_child_exit = 2;
778 tm_notify_child_exit = time(NULL);
779 }
780 else if (notify_child_exit == 2 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_KILL_TIMEOUT)
781 {
782 log_error("Main process prepare to exit without waiting for %d child process any longer\n", SYS_child_process_count);
783 SYS_child_process_count = 0;
784 }
785 }
786
787 if (SYS_conf_reload && !SYS_server_exit)
788 {
789 SYS_conf_reload = 0;
790
791 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
792 sd_notify(0, "RELOADING=1");
793 #endif
794
795 // Restart log
796 if (log_restart() < 0)
797 {
798 log_error("Restart logging failed\n");
799 }
800
801 // Reload configuration
802 if (load_conf(CONF_BBSD) < 0)
803 {
804 log_error("Reload conf failed\n");
805 }
806
807 // Reload BWF config
808 if (bwf_load(CONF_BWF) < 0)
809 {
810 log_error("Reload BWF conf failed\n");
811 }
812
813 // Get EULA modification tm
814 if (stat(DATA_EULA, &file_stat) == -1)
815 {
816 log_error("stat(%s) error\n", DATA_EULA, errno);
817 }
818 else
819 {
820 BBS_eula_tm = file_stat.st_mtim.tv_sec;
821 }
822
823 if (detach_menu_shm(&bbs_menu) < 0)
824 {
825 log_error("detach_menu_shm(bbs_menu) error\n");
826 }
827 if (load_menu(&bbs_menu, CONF_MENU) < 0)
828 {
829 log_error("load_menu(bbs_menu) error\n");
830 unload_menu(&bbs_menu);
831 }
832
833 if (detach_menu_shm(&top10_menu) < 0)
834 {
835 log_error("detach_menu_shm(top10_menu) error\n");
836 }
837 if (load_menu(&top10_menu, CONF_TOP10_MENU) < 0)
838 {
839 log_error("load_menu(top10_menu) error\n");
840 unload_menu(&top10_menu);
841 }
842 top10_menu.allow_exit = 1;
843
844 for (int i = 0; i < data_files_load_startup_count; i++)
845 {
846 if (load_file(data_files_load_startup[i]) < 0)
847 {
848 log_error("load_file(%s) error\n", data_files_load_startup[i]);
849 }
850 }
851
852 // Load section config and gen_ex
853 if (load_section_config_from_db(1) < 0)
854 {
855 log_error("load_section_config_from_db(1) error\n");
856 }
857
858 // Notify child processes to reload configuration
859 if (kill(0, SIGUSR1) < 0)
860 {
861 log_error("Send SIGUSR1 signal failed (%d)\n", errno);
862 }
863
864 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
865 sd_notify(0, "READY=1");
866 #endif
867 }
868
869 #ifdef HAVE_SYS_EPOLL_H
870 nfds = epoll_wait(epollfd_server, events, MAX_EVENTS, 100); // 0.1 second
871 ret = nfds;
872 #else
873 pfds[0].fd = socket_server[0];
874 pfds[0].events = POLLIN;
875 pfds[1].fd = socket_server[1];
876 pfds[1].events = POLLIN;
877 nfds = 2;
878 ret = poll(pfds, (nfds_t)nfds, 100); // 0.1 second
879 #endif
880 if (ret < 0)
881 {
882 if (errno != EINTR)
883 {
884 #ifdef HAVE_SYS_EPOLL_H
885 log_error("epoll_wait() error (%d)\n", errno);
886 #else
887 log_error("poll() error (%d)\n", errno);
888 #endif
889 break;
890 }
891 continue;
892 }
893
894 // Stop accept new connection on exit
895 if (SYS_server_exit)
896 {
897 continue;
898 }
899
900 for (int i = 0; i < nfds; i++)
901 {
902 #ifdef HAVE_SYS_EPOLL_H
903 if (events[i].data.fd == socket_server[0] || events[i].data.fd == socket_server[1])
904 #else
905 if ((pfds[i].fd == socket_server[0] || pfds[i].fd == socket_server[1]) && (pfds[i].revents & POLLIN))
906 #endif
907 {
908 #ifdef HAVE_SYS_EPOLL_H
909 SSH_v2 = (events[i].data.fd == socket_server[1] ? 1 : 0);
910 #else
911 SSH_v2 = (pfds[i].fd == socket_server[1] ? 1 : 0);
912 #endif
913
914 while (!SYS_server_exit) // Accept all incoming connections until error
915 {
916 addrlen = sizeof(sin);
917 socket_client = accept(socket_server[SSH_v2], (struct sockaddr *)&sin, (socklen_t *)&addrlen);
918 if (socket_client < 0)
919 {
920 if (errno == EAGAIN || errno == EWOULDBLOCK)
921 {
922 break;
923 }
924 else if (errno == EINTR)
925 {
926 continue;
927 }
928 else
929 {
930 log_error("accept(socket_server) error (%d)\n", errno);
931 break;
932 }
933 }
934
935 strncpy(hostaddr_client, inet_ntoa(sin.sin_addr), sizeof(hostaddr_client) - 1);
936 hostaddr_client[sizeof(hostaddr_client) - 1] = '\0';
937
938 port_client = ntohs(sin.sin_port);
939
940 if (SYS_child_process_count - 1 < BBS_max_client)
941 {
942 j = 0;
943 ret = hash_dict_get(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, (int64_t *)&j);
944 if (ret < 0)
945 {
946 log_error("hash_dict_get(hash_dict_sockaddr_count, %s) error\n", hostaddr_client);
947 }
948
949 if (j < BBS_max_client_per_ip)
950 {
951 if ((pid = fork_server()) < 0)
952 {
953 log_error("fork_server() error\n");
954 }
955 else if (pid > 0)
956 {
957 ret = hash_dict_set(hash_dict_pid_sockaddr, (uint64_t)pid, sin.sin_addr.s_addr);
958 if (ret < 0)
959 {
960 log_error("hash_dict_set(hash_dict_pid_sockaddr, %d, %s) error\n", pid, hostaddr_client);
961 }
962
963 if (j == 0)
964 {
965 // First connection from this IP
966 log_common("Accept %s connection from %s:%d\n",
967 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client);
968
969 ret = hash_dict_set(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, 1);
970 if (ret < 0)
971 {
972 log_error("hash_dict_set(hash_dict_sockaddr_count, %s, 1) error\n", hostaddr_client);
973 }
974 }
975 else
976 {
977 // Increase connection count from this IP
978 log_common("Accept %s connection from %s:%d, already have %d connections\n",
979 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, j);
980
981 ret = hash_dict_inc(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, 1);
982 if (ret <= 0)
983 {
984 log_error("hash_dict_inc(hash_dict_sockaddr_count, %s, 1) error: %d\n", hostaddr_client, ret);
985 }
986 }
987 }
988 }
989 else
990 {
991 log_error("Rejected %s connection from %s:%d over limit per IP (%d >= %d)\n",
992 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, j, BBS_max_client_per_ip);
993 }
994 }
995 else
996 {
997 log_error("Rejected %s connection from %s:%d over limit (%d >= %d)\n",
998 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, SYS_child_process_count - 1, BBS_max_client);
999 }
1000
1001 if (close(socket_client) == -1)
1002 {
1003 log_error("close(socket_lient) error (%d)\n", errno);
1004 }
1005 }
1006 }
1007 }
1008 }
1009
1010 #ifdef HAVE_SYS_EPOLL_H
1011 if (close(epollfd_server) < 0)
1012 {
1013 log_error("close(epollfd_server) error (%d)\n");
1014 }
1015 #endif
1016
1017 for (i = 0; i < 2; i++)
1018 {
1019 if (close(socket_server[i]) == -1)
1020 {
1021 log_error("Close server socket failed\n");
1022 }
1023 }
1024
1025 hash_dict_destroy(hash_dict_pid_sockaddr);
1026 hash_dict_destroy(hash_dict_sockaddr_count);
1027
1028 ssh_bind_free(sshbind);
1029 ssh_finalize();
1030
1031 return 0;
1032 }

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