/[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.115 - (show annotations)
Sat Jan 3 10:27:14 2026 UTC (2 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.114: +1 -1 lines
Content type: text/x-csrc
Update copyright info

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

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