/[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.116 - (show annotations)
Wed Jan 7 14:37:55 2026 UTC (2 months, 1 week ago) by sysadm
Branch: MAIN
CVS Tags: HEAD
Changes since 1.115: +2 -1 lines
Content type: text/x-csrc
Add constant value for socket listen backlog

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

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