/[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.108 - (show annotations)
Wed Dec 17 07:42:32 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.107: +9 -10 lines
Content type: text/x-csrc
Refine

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 pid_t pid;
534 int ssh_key_valid = 0;
535 int ssh_log_level = SSH_LOG_NOLOG;
536
537 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
538 int sd_notify_stopping = 0;
539 #endif
540
541 ssh_init();
542
543 sshbind = ssh_bind_new();
544
545 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_RSA_KEY_FILE) < 0)
546 {
547 log_error("Error loading SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE);
548 }
549 else
550 {
551 ssh_key_valid = 1;
552 }
553 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ED25519_KEY_FILE) < 0)
554 {
555 log_error("Error loading SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE);
556 }
557 else
558 {
559 ssh_key_valid = 1;
560 }
561 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ECDSA_KEY_FILE) < 0)
562 {
563 log_error("Error loading SSH ECDSA key: %s\n", SSH_HOST_ECDSA_KEY_FILE);
564 }
565 else
566 {
567 ssh_key_valid = 1;
568 }
569
570 if (!ssh_key_valid)
571 {
572 log_error("Error: no valid SSH host key\n");
573 ssh_bind_free(sshbind);
574 return -1;
575 }
576
577 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 ||
578 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 ||
579 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "+ssh-rsa") < 0 ||
580 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0)
581 {
582 log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind));
583 ssh_bind_free(sshbind);
584 return -1;
585 }
586
587 #ifdef HAVE_SYS_EPOLL_H
588 epollfd_server = epoll_create1(0);
589 if (epollfd_server == -1)
590 {
591 log_error("epoll_create1() error (%d)\n", errno);
592 return -1;
593 }
594 #endif
595
596 // Server socket
597 for (int i = 0; i < 2; i++)
598 {
599 socket_server[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
600
601 if (socket_server[i] < 0)
602 {
603 log_error("Create socket_server error (%d)\n", errno);
604 return -1;
605 }
606
607 sin.sin_family = AF_INET;
608 sin.sin_addr.s_addr = (hostaddr[0] != '\0' ? inet_addr(hostaddr) : INADDR_ANY);
609 sin.sin_port = htons(port[i]);
610
611 // Reuse address and port
612 flags_server[i] = 1;
613 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEADDR, &flags_server[i], sizeof(flags_server[i])) < 0)
614 {
615 log_error("setsockopt SO_REUSEADDR error (%d)\n", errno);
616 }
617 #if defined(SO_REUSEPORT)
618 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEPORT, &flags_server[i], sizeof(flags_server[i])) < 0)
619 {
620 log_error("setsockopt SO_REUSEPORT error (%d)\n", errno);
621 }
622 #endif
623
624 if (bind(socket_server[i], (struct sockaddr *)&sin, sizeof(sin)) < 0)
625 {
626 log_error("Bind address %s:%u error (%d)\n",
627 inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), errno);
628 return -1;
629 }
630
631 if (listen(socket_server[i], 10) < 0)
632 {
633 log_error("Telnet socket listen error (%d)\n", errno);
634 return -1;
635 }
636
637 log_common("Listening at %s:%u\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
638
639 #ifdef HAVE_SYS_EPOLL_H
640 ev.events = EPOLLIN;
641 ev.data.fd = socket_server[i];
642 if (epoll_ctl(epollfd_server, EPOLL_CTL_ADD, socket_server[i], &ev) == -1)
643 {
644 log_error("epoll_ctl(socket_server[%d]) error (%d)\n", i, errno);
645 if (close(epollfd_server) < 0)
646 {
647 log_error("close(epoll) error (%d)\n");
648 }
649 return -1;
650 }
651 #endif
652
653 flags_server[i] = fcntl(socket_server[i], F_GETFL, 0);
654 fcntl(socket_server[i], F_SETFL, flags_server[i] | O_NONBLOCK);
655 }
656
657 ssh_bind_set_blocking(sshbind, 0);
658
659 hash_dict_pid_sockaddr = hash_dict_create(MAX_CLIENT_LIMIT);
660 if (hash_dict_pid_sockaddr == NULL)
661 {
662 log_error("hash_dict_create(hash_dict_pid_sockaddr) error\n");
663 return -1;
664 }
665 hash_dict_sockaddr_count = hash_dict_create(MAX_CLIENT_LIMIT);
666 if (hash_dict_sockaddr_count == NULL)
667 {
668 log_error("hash_dict_create(hash_dict_sockaddr_count) error\n");
669 return -1;
670 }
671
672 // Startup complete
673 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
674 sd_notifyf(0, "READY=1\n"
675 "STATUS=Listening at %s:%d (Telnet) and %s:%d (SSH2)\n"
676 "MAINPID=%d",
677 hostaddr, port[0], hostaddr, port[1], getpid());
678 #endif
679
680 while (!SYS_server_exit || SYS_child_process_count > 0)
681 {
682 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
683 if (SYS_server_exit && !sd_notify_stopping)
684 {
685 sd_notify(0, "STOPPING=1");
686 sd_notify_stopping = 1;
687 }
688 #endif
689
690 while ((SYS_child_exit || SYS_server_exit) && SYS_child_process_count > 0)
691 {
692 SYS_child_exit = 0;
693
694 pid = waitpid(-1, &ret, WNOHANG);
695 if (pid > 0)
696 {
697 SYS_child_exit = 1; // Retry waitid
698 SYS_child_process_count--;
699
700 if (WIFEXITED(ret))
701 {
702 log_common("Child process (%d) exited, status=%d\n", pid, WEXITSTATUS(ret));
703 }
704 else if (WIFSIGNALED(ret))
705 {
706 log_common("Child process (%d) is killed, status=%d\n", pid, WTERMSIG(ret));
707 }
708 else
709 {
710 log_common("Child process (%d) exited abnormally, status=%d\n", pid, ret);
711 }
712
713 if (pid != section_list_loader_pid)
714 {
715 int64_t j = 0;
716 ret = hash_dict_get(hash_dict_pid_sockaddr, (uint64_t)pid, &j);
717 if (ret < 0)
718 {
719 log_error("hash_dict_get(hash_dict_pid_sockaddr, %d) error\n", pid);
720 }
721 else
722 {
723 ret = hash_dict_inc(hash_dict_sockaddr_count, (in_addr_t)j, -1);
724 if (ret <= 0)
725 {
726 log_error("hash_dict_inc(hash_dict_sockaddr_count, %lu, -1) error: %d\n", (in_addr_t)j, ret);
727 }
728
729 ret = hash_dict_del(hash_dict_pid_sockaddr, (uint64_t)pid);
730 if (ret < 0)
731 {
732 log_error("hash_dict_del(hash_dict_pid_sockaddr, %lu) error\n", (uint64_t)pid);
733 }
734 }
735 }
736 }
737 else if (pid == 0)
738 {
739 break;
740 }
741 else if (pid < 0)
742 {
743 log_error("Error in waitpid(): %d\n", errno);
744 break;
745 }
746 }
747
748 if (SYS_server_exit && !SYS_child_exit && SYS_child_process_count > 0)
749 {
750 if (notify_child_exit == 0)
751 {
752 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
753 sd_notifyf(0, "STATUS=Notify %d child process to exit", SYS_child_process_count);
754 log_common("Notify %d child process to exit\n", SYS_child_process_count);
755 #endif
756
757 if (kill(0, SIGTERM) < 0)
758 {
759 log_error("Send SIGTERM signal failed (%d)\n", errno);
760 }
761
762 notify_child_exit = 1;
763 tm_notify_child_exit = time(NULL);
764 }
765 else if (notify_child_exit == 1 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_EXIT_TIMEOUT)
766 {
767 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
768 sd_notifyf(0, "STATUS=Kill %d child process", SYS_child_process_count);
769 #endif
770
771 if (kill(0, SIGKILL) < 0)
772 {
773 log_error("Send SIGKILL signal failed (%d)\n", errno);
774 }
775
776 notify_child_exit = 2;
777 tm_notify_child_exit = time(NULL);
778 }
779 else if (notify_child_exit == 2 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_KILL_TIMEOUT)
780 {
781 log_error("Main process prepare to exit without waiting for %d child process any longer\n", SYS_child_process_count);
782 SYS_child_process_count = 0;
783 }
784 }
785
786 if (SYS_conf_reload && !SYS_server_exit)
787 {
788 SYS_conf_reload = 0;
789
790 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
791 sd_notify(0, "RELOADING=1");
792 #endif
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