/[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.111 - (show annotations)
Thu Dec 18 14:47:00 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.110: +2 -0 lines
Content type: text/x-csrc
Refine and bug fix

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 log_debug("ssh_event_dopoll() error: %s\n", ssh_get_error(SSH_session));
395 goto cleanup;
396 }
397 }
398
399 if (cb_data.error)
400 {
401 log_error("SSH auth error, tried %d times\n", cb_data.tries);
402 goto cleanup;
403 }
404
405 ssh_set_channel_callbacks(SSH_channel, &channel_cb);
406
407 do
408 {
409 ret = ssh_event_dopoll(event, 100); // 0.1 second
410 if (ret == SSH_ERROR)
411 {
412 ssh_channel_close(SSH_channel);
413 }
414
415 if (ret == SSH_AGAIN) // loop until SSH connection is fully established
416 {
417 /* Executed only once, once the child process starts. */
418 cdata.event = event;
419 break;
420 }
421 } while (ssh_channel_is_open(SSH_channel));
422
423 ssh_timeout = 0;
424 if (ssh_options_set(SSH_session, SSH_OPTIONS_TIMEOUT, &ssh_timeout) < 0)
425 {
426 log_error("Error setting SSH options: %s\n", ssh_get_error(SSH_session));
427 goto cleanup;
428 }
429
430 ssh_set_blocking(SSH_session, 0);
431 }
432
433 // Redirect Input
434 if (dup2(socket_client, STDIN_FILENO) == -1)
435 {
436 log_error("Redirect stdin to client socket failed\n");
437 goto cleanup;
438 }
439
440 // Redirect Output
441 if (dup2(socket_client, STDOUT_FILENO) == -1)
442 {
443 log_error("Redirect stdout to client socket failed\n");
444 goto cleanup;
445 }
446
447 if (io_init() < 0)
448 {
449 log_error("io_init() error\n");
450 goto cleanup;
451 }
452
453 SYS_child_process_count = 0;
454
455 // BWF compile
456 if (bwf_compile() < 0)
457 {
458 log_error("bwf_compile() error\n");
459 goto cleanup;
460 }
461
462 bbs_main();
463
464 cleanup:
465 // Child process exit
466 SYS_server_exit = 1;
467
468 if (SSH_v2)
469 {
470 if (cdata.pty_master != -1)
471 {
472 close(cdata.pty_master);
473 }
474 if (cdata.child_stdin != -1)
475 {
476 close(cdata.child_stdin);
477 }
478 if (cdata.child_stdout != -1)
479 {
480 close(cdata.child_stdout);
481 }
482 if (cdata.child_stderr != -1)
483 {
484 close(cdata.child_stderr);
485 }
486
487 ssh_channel_free(SSH_channel);
488 ssh_disconnect(SSH_session);
489 }
490 else if (close(socket_client) == -1)
491 {
492 log_error("Close client socket failed\n");
493 }
494
495 ssh_free(SSH_session);
496 ssh_finalize();
497
498 // BWF cleanup
499 bwf_cleanup();
500
501 // Close Input and Output for client
502 io_cleanup();
503 close(STDIN_FILENO);
504 close(STDOUT_FILENO);
505
506 log_common("Process exit normally\n");
507 log_end();
508
509 _exit(0);
510
511 return 0;
512 }
513
514 int net_server(const char *hostaddr, in_port_t port[])
515 {
516 struct stat file_stat;
517 unsigned int addrlen;
518 int ret;
519 int flags_server[2];
520 struct sockaddr_in sin;
521
522 #ifdef HAVE_SYS_EPOLL_H
523 struct epoll_event ev, events[MAX_EVENTS];
524 #else
525 struct pollfd pfds[2];
526 #endif
527
528 int nfds;
529 int notify_child_exit = 0;
530 time_t tm_notify_child_exit = time(NULL);
531 pid_t pid;
532 int ssh_key_valid = 0;
533 int ssh_log_level = SSH_LOG_NOLOG;
534
535 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
536 int sd_notify_stopping = 0;
537 #endif
538
539 ssh_init();
540
541 sshbind = ssh_bind_new();
542
543 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_RSA_KEY_FILE) < 0)
544 {
545 log_error("Error loading SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE);
546 }
547 else
548 {
549 ssh_key_valid = 1;
550 }
551 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ED25519_KEY_FILE) < 0)
552 {
553 log_error("Error loading SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE);
554 }
555 else
556 {
557 ssh_key_valid = 1;
558 }
559 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ECDSA_KEY_FILE) < 0)
560 {
561 log_error("Error loading SSH ECDSA key: %s\n", SSH_HOST_ECDSA_KEY_FILE);
562 }
563 else
564 {
565 ssh_key_valid = 1;
566 }
567
568 if (!ssh_key_valid)
569 {
570 log_error("Error: no valid SSH host key\n");
571 ssh_bind_free(sshbind);
572 return -1;
573 }
574
575 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 ||
576 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 ||
577 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "+ssh-ed25519,ecdsa-sha2-nistp256,ssh-rsa") < 0 ||
578 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0)
579 {
580 log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind));
581 ssh_bind_free(sshbind);
582 return -1;
583 }
584
585 #ifdef HAVE_SYS_EPOLL_H
586 epollfd_server = epoll_create1(0);
587 if (epollfd_server == -1)
588 {
589 log_error("epoll_create1() error (%d)\n", errno);
590 return -1;
591 }
592 #endif
593
594 // Server socket
595 for (int i = 0; i < 2; i++)
596 {
597 socket_server[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
598
599 if (socket_server[i] < 0)
600 {
601 log_error("Create socket_server error (%d)\n", errno);
602 return -1;
603 }
604
605 sin.sin_family = AF_INET;
606 sin.sin_addr.s_addr = (hostaddr[0] != '\0' ? inet_addr(hostaddr) : INADDR_ANY);
607 sin.sin_port = htons(port[i]);
608
609 // Reuse address and port
610 flags_server[i] = 1;
611 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEADDR, &flags_server[i], sizeof(flags_server[i])) < 0)
612 {
613 log_error("setsockopt SO_REUSEADDR error (%d)\n", errno);
614 }
615 #if defined(SO_REUSEPORT)
616 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEPORT, &flags_server[i], sizeof(flags_server[i])) < 0)
617 {
618 log_error("setsockopt SO_REUSEPORT error (%d)\n", errno);
619 }
620 #endif
621
622 if (bind(socket_server[i], (struct sockaddr *)&sin, sizeof(sin)) < 0)
623 {
624 log_error("Bind address %s:%u error (%d)\n",
625 inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), errno);
626 return -1;
627 }
628
629 if (listen(socket_server[i], 10) < 0)
630 {
631 log_error("Telnet socket listen error (%d)\n", errno);
632 return -1;
633 }
634
635 log_common("Listening at %s:%u\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
636
637 #ifdef HAVE_SYS_EPOLL_H
638 ev.events = EPOLLIN;
639 ev.data.fd = socket_server[i];
640 if (epoll_ctl(epollfd_server, EPOLL_CTL_ADD, socket_server[i], &ev) == -1)
641 {
642 log_error("epoll_ctl(socket_server[%d]) error (%d)\n", i, errno);
643 if (close(epollfd_server) < 0)
644 {
645 log_error("close(epoll) error (%d)\n");
646 }
647 return -1;
648 }
649 #endif
650
651 flags_server[i] = fcntl(socket_server[i], F_GETFL, 0);
652 fcntl(socket_server[i], F_SETFL, flags_server[i] | O_NONBLOCK);
653 }
654
655 ssh_bind_set_blocking(sshbind, 0);
656
657 hash_dict_pid_sockaddr = hash_dict_create(MAX_CLIENT_LIMIT);
658 if (hash_dict_pid_sockaddr == NULL)
659 {
660 log_error("hash_dict_create(hash_dict_pid_sockaddr) error\n");
661 return -1;
662 }
663 hash_dict_sockaddr_count = hash_dict_create(MAX_CLIENT_LIMIT);
664 if (hash_dict_sockaddr_count == NULL)
665 {
666 log_error("hash_dict_create(hash_dict_sockaddr_count) error\n");
667 return -1;
668 }
669
670 // Startup complete
671 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
672 sd_notifyf(0, "READY=1\n"
673 "STATUS=Listening at %s:%d (Telnet) and %s:%d (SSH2)\n"
674 "MAINPID=%d",
675 hostaddr, port[0], hostaddr, port[1], getpid());
676 #endif
677
678 while (!SYS_server_exit || SYS_child_process_count > 0)
679 {
680 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
681 if (SYS_server_exit && !sd_notify_stopping)
682 {
683 sd_notify(0, "STOPPING=1");
684 sd_notify_stopping = 1;
685 }
686 #endif
687
688 while ((SYS_child_exit || SYS_server_exit) && SYS_child_process_count > 0)
689 {
690 SYS_child_exit = 0;
691
692 pid = waitpid(-1, &ret, WNOHANG);
693 if (pid > 0)
694 {
695 SYS_child_exit = 1; // Retry waitid
696 SYS_child_process_count--;
697
698 if (WIFEXITED(ret))
699 {
700 log_common("Child process (%d) exited, status=%d\n", pid, WEXITSTATUS(ret));
701 }
702 else if (WIFSIGNALED(ret))
703 {
704 log_common("Child process (%d) is killed, status=%d\n", pid, WTERMSIG(ret));
705 }
706 else
707 {
708 log_common("Child process (%d) exited abnormally, status=%d\n", pid, ret);
709 }
710
711 if (pid != section_list_loader_pid)
712 {
713 int64_t j = 0;
714 ret = hash_dict_get(hash_dict_pid_sockaddr, (uint64_t)pid, &j);
715 if (ret < 0)
716 {
717 log_error("hash_dict_get(hash_dict_pid_sockaddr, %d) error\n", pid);
718 }
719 else
720 {
721 ret = hash_dict_inc(hash_dict_sockaddr_count, (in_addr_t)j, -1);
722 if (ret <= 0)
723 {
724 log_error("hash_dict_inc(hash_dict_sockaddr_count, %lu, -1) error: %d\n", (in_addr_t)j, ret);
725 }
726
727 ret = hash_dict_del(hash_dict_pid_sockaddr, (uint64_t)pid);
728 if (ret < 0)
729 {
730 log_error("hash_dict_del(hash_dict_pid_sockaddr, %lu) error\n", (uint64_t)pid);
731 }
732 }
733 }
734 }
735 else if (pid == 0)
736 {
737 break;
738 }
739 else if (pid < 0)
740 {
741 log_error("Error in waitpid(): %d\n", errno);
742 break;
743 }
744 }
745
746 if (SYS_server_exit && !SYS_child_exit && SYS_child_process_count > 0)
747 {
748 if (notify_child_exit == 0)
749 {
750 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
751 sd_notifyf(0, "STATUS=Notify %d child process to exit", SYS_child_process_count);
752 log_common("Notify %d child process to exit\n", SYS_child_process_count);
753 #endif
754
755 if (kill(0, SIGTERM) < 0)
756 {
757 log_error("Send SIGTERM signal failed (%d)\n", errno);
758 }
759
760 notify_child_exit = 1;
761 tm_notify_child_exit = time(NULL);
762 }
763 else if (notify_child_exit == 1 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_EXIT_TIMEOUT)
764 {
765 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
766 sd_notifyf(0, "STATUS=Kill %d child process", SYS_child_process_count);
767 #endif
768
769 if (kill(0, SIGKILL) < 0)
770 {
771 log_error("Send SIGKILL signal failed (%d)\n", errno);
772 }
773
774 notify_child_exit = 2;
775 tm_notify_child_exit = time(NULL);
776 }
777 else if (notify_child_exit == 2 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_KILL_TIMEOUT)
778 {
779 log_error("Main process prepare to exit without waiting for %d child process any longer\n", SYS_child_process_count);
780 SYS_child_process_count = 0;
781 }
782 }
783
784 if (SYS_conf_reload && !SYS_server_exit)
785 {
786 SYS_conf_reload = 0;
787
788 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
789 sd_notify(0, "RELOADING=1");
790 #endif
791
792 log_common("Reload configuration\n");
793
794 // Restart log
795 if (log_restart() < 0)
796 {
797 log_error("Restart logging failed\n");
798 }
799
800 // Reload configuration
801 if (load_conf(CONF_BBSD) < 0)
802 {
803 log_error("Reload conf failed\n");
804 }
805
806 // Reload BWF config
807 if (bwf_load(CONF_BWF) < 0)
808 {
809 log_error("Reload BWF conf failed\n");
810 }
811
812 // Get EULA modification tm
813 if (stat(DATA_EULA, &file_stat) == -1)
814 {
815 log_error("stat(%s) error\n", DATA_EULA, errno);
816 }
817 else
818 {
819 BBS_eula_tm = file_stat.st_mtim.tv_sec;
820 }
821
822 if (detach_menu_shm(&bbs_menu) < 0)
823 {
824 log_error("detach_menu_shm(bbs_menu) error\n");
825 }
826 if (load_menu(&bbs_menu, CONF_MENU) < 0)
827 {
828 log_error("load_menu(bbs_menu) error\n");
829 unload_menu(&bbs_menu);
830 }
831
832 if (detach_menu_shm(&top10_menu) < 0)
833 {
834 log_error("detach_menu_shm(top10_menu) error\n");
835 }
836 if (load_menu(&top10_menu, CONF_TOP10_MENU) < 0)
837 {
838 log_error("load_menu(top10_menu) error\n");
839 unload_menu(&top10_menu);
840 }
841 top10_menu.allow_exit = 1;
842
843 for (int i = 0; i < data_files_load_startup_count; i++)
844 {
845 if (load_file(data_files_load_startup[i]) < 0)
846 {
847 log_error("load_file(%s) error\n", data_files_load_startup[i]);
848 }
849 }
850
851 // Load section config and gen_ex
852 if (load_section_config_from_db(1) < 0)
853 {
854 log_error("load_section_config_from_db(1) error\n");
855 }
856
857 // Notify child processes to reload configuration
858 if (kill(0, SIGUSR1) < 0)
859 {
860 log_error("Send SIGUSR1 signal failed (%d)\n", errno);
861 }
862
863 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
864 sd_notify(0, "READY=1");
865 #endif
866 }
867
868 #ifdef HAVE_SYS_EPOLL_H
869 nfds = epoll_wait(epollfd_server, events, MAX_EVENTS, 100); // 0.1 second
870 ret = nfds;
871 #else
872 pfds[0].fd = socket_server[0];
873 pfds[0].events = POLLIN;
874 pfds[1].fd = socket_server[1];
875 pfds[1].events = POLLIN;
876 nfds = 2;
877 ret = poll(pfds, (nfds_t)nfds, 100); // 0.1 second
878 #endif
879 if (ret < 0)
880 {
881 if (errno != EINTR)
882 {
883 #ifdef HAVE_SYS_EPOLL_H
884 log_error("epoll_wait() error (%d)\n", errno);
885 #else
886 log_error("poll() error (%d)\n", errno);
887 #endif
888 break;
889 }
890 continue;
891 }
892
893 // Stop accept new connection on exit
894 if (SYS_server_exit)
895 {
896 continue;
897 }
898
899 for (int i = 0; i < nfds; i++)
900 {
901 #ifdef HAVE_SYS_EPOLL_H
902 if (events[i].data.fd == socket_server[0] || events[i].data.fd == socket_server[1])
903 #else
904 if ((pfds[i].fd == socket_server[0] || pfds[i].fd == socket_server[1]) && (pfds[i].revents & POLLIN))
905 #endif
906 {
907 #ifdef HAVE_SYS_EPOLL_H
908 SSH_v2 = (events[i].data.fd == socket_server[1] ? 1 : 0);
909 #else
910 SSH_v2 = (pfds[i].fd == socket_server[1] ? 1 : 0);
911 #endif
912
913 while (!SYS_server_exit) // Accept all incoming connections until error
914 {
915 addrlen = sizeof(sin);
916 socket_client = accept(socket_server[SSH_v2], (struct sockaddr *)&sin, (socklen_t *)&addrlen);
917 if (socket_client < 0)
918 {
919 if (errno == EAGAIN || errno == EWOULDBLOCK)
920 {
921 break;
922 }
923 else if (errno == EINTR)
924 {
925 continue;
926 }
927 else
928 {
929 log_error("accept(socket_server) error (%d)\n", errno);
930 break;
931 }
932 }
933
934 strncpy(hostaddr_client, inet_ntoa(sin.sin_addr), sizeof(hostaddr_client) - 1);
935 hostaddr_client[sizeof(hostaddr_client) - 1] = '\0';
936
937 port_client = ntohs(sin.sin_port);
938
939 if (SYS_child_process_count - 1 < BBS_max_client)
940 {
941 int64_t j = 0;
942 ret = hash_dict_get(hash_dict_sockaddr_count, sin.sin_addr.s_addr, &j);
943 if (ret < 0)
944 {
945 log_error("hash_dict_get(hash_dict_sockaddr_count, %s) error\n", hostaddr_client);
946 }
947
948 if (j < BBS_max_client_per_ip)
949 {
950 if ((pid = fork_server()) < 0)
951 {
952 log_error("fork_server() error\n");
953 }
954 else if (pid > 0)
955 {
956 ret = hash_dict_set(hash_dict_pid_sockaddr, (uint64_t)pid, sin.sin_addr.s_addr);
957 if (ret < 0)
958 {
959 log_error("hash_dict_set(hash_dict_pid_sockaddr, %lu, %s) error\n", (uint64_t)pid, hostaddr_client);
960 }
961
962 if (j == 0)
963 {
964 // First connection from this IP
965 log_common("Accept %s connection from %s:%d\n",
966 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client);
967
968 ret = hash_dict_set(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, 1);
969 if (ret < 0)
970 {
971 log_error("hash_dict_set(hash_dict_sockaddr_count, %s, 1) error\n", hostaddr_client);
972 }
973 }
974 else
975 {
976 // Increase connection count from this IP
977 log_common("Accept %s connection from %s:%d, already have %d connections\n",
978 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, j);
979
980 ret = hash_dict_inc(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, 1);
981 if (ret <= 0)
982 {
983 log_error("hash_dict_inc(hash_dict_sockaddr_count, %s, 1) error: %d\n", hostaddr_client, ret);
984 }
985 }
986 }
987 }
988 else
989 {
990 log_error("Rejected %s connection from %s:%d over limit per IP (%d >= %d)\n",
991 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, j, BBS_max_client_per_ip);
992 }
993 }
994 else
995 {
996 log_error("Rejected %s connection from %s:%d over limit (%d >= %d)\n",
997 (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, SYS_child_process_count - 1, BBS_max_client);
998 }
999
1000 if (close(socket_client) == -1)
1001 {
1002 log_error("close(socket_lient) error (%d)\n", errno);
1003 }
1004 }
1005 }
1006 }
1007 }
1008
1009 #ifdef HAVE_SYS_EPOLL_H
1010 if (close(epollfd_server) < 0)
1011 {
1012 log_error("close(epollfd_server) error (%d)\n");
1013 }
1014 #endif
1015
1016 for (int i = 0; i < 2; i++)
1017 {
1018 if (close(socket_server[i]) == -1)
1019 {
1020 log_error("Close server socket failed\n");
1021 }
1022 }
1023
1024 hash_dict_destroy(hash_dict_pid_sockaddr);
1025 hash_dict_destroy(hash_dict_sockaddr_count);
1026
1027 ssh_bind_free(sshbind);
1028 ssh_finalize();
1029
1030 return 0;
1031 }

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