/[LeafOK_CVS]/lbbs/src/net_server.c
ViewVC logotype

Annotation of /lbbs/src/net_server.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.106 - (hide annotations)
Wed Dec 17 03:44:29 2025 UTC (2 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.105: +24 -8 lines
Content type: text/x-csrc
Apply different handling to first connection and concurrent connections from the same IP.

1 sysadm 1.77 /* SPDX-License-Identifier: GPL-3.0-or-later */
2     /*
3     * net_server
4     * - network server with SSH support
5     *
6 sysadm 1.78 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 sysadm 1.77 */
8 sysadm 1.1
9 sysadm 1.81 #ifdef HAVE_CONFIG_H
10     #include "config.h"
11     #endif
12    
13 sysadm 1.62 #include "bbs.h"
14     #include "bbs_main.h"
15 sysadm 1.80 #include "bwf.h"
16 sysadm 1.1 #include "common.h"
17 sysadm 1.62 #include "database.h"
18     #include "file_loader.h"
19 sysadm 1.82 #include "hash_dict.h"
20 sysadm 1.7 #include "io.h"
21 sysadm 1.44 #include "init.h"
22 sysadm 1.62 #include "log.h"
23     #include "login.h"
24 sysadm 1.19 #include "menu.h"
25 sysadm 1.62 #include "net_server.h"
26 sysadm 1.67 #include "section_list.h"
27 sysadm 1.42 #include "section_list_loader.h"
28 sysadm 1.20 #include <errno.h>
29     #include <fcntl.h>
30 sysadm 1.74 #include <pty.h>
31 sysadm 1.19 #include <signal.h>
32 sysadm 1.20 #include <stdlib.h>
33 sysadm 1.62 #include <string.h>
34 sysadm 1.19 #include <unistd.h>
35 sysadm 1.74 #include <utmp.h>
36 sysadm 1.9 #include <arpa/inet.h>
37 sysadm 1.62 #include <libssh/callbacks.h>
38 sysadm 1.55 #include <libssh/libssh.h>
39     #include <libssh/server.h>
40 sysadm 1.62 #include <netinet/in.h>
41 sysadm 1.89 #include <sys/ioctl.h>
42 sysadm 1.62 #include <sys/socket.h>
43 sysadm 1.100 #include <sys/stat.h>
44 sysadm 1.61 #include <sys/types.h>
45     #include <sys/wait.h>
46 sysadm 1.86
47 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
48     #include <sys/epoll.h>
49     #else
50     #include <poll.h>
51     #endif
52    
53 sysadm 1.86 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
54 sysadm 1.61 #include <systemd/sd-daemon.h>
55 sysadm 1.86 #endif
56 sysadm 1.3
57 sysadm 1.79 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 sysadm 1.70
65 sysadm 1.74 /* A userdata struct for session. */
66     struct session_data_struct
67 sysadm 1.55 {
68     int tries;
69     int error;
70     };
71    
72 sysadm 1.74 /* 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 sysadm 1.87 static int socket_server[2];
92     static int socket_client;
93 sysadm 1.88
94     #ifdef HAVE_SYS_EPOLL_H
95 sysadm 1.87 static int epollfd_server = -1;
96 sysadm 1.88 #endif
97    
98 sysadm 1.87 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 sysadm 1.55 static int auth_password(ssh_session session, const char *user,
106     const char *password, void *userdata)
107     {
108 sysadm 1.74 struct session_data_struct *sdata = (struct session_data_struct *)userdata;
109 sysadm 1.55 int ret;
110    
111     if (strcmp(user, "guest") == 0)
112     {
113 sysadm 1.57 ret = load_guest_info();
114 sysadm 1.55 }
115     else
116     {
117 sysadm 1.57 ret = check_user(user, password);
118 sysadm 1.101 if (ret == 2) // Enforce update user agreement
119     {
120     BBS_update_eula = 1;
121     ret = 0;
122     }
123 sysadm 1.55 }
124    
125     if (ret == 0)
126     {
127     return SSH_AUTH_SUCCESS;
128     }
129    
130 sysadm 1.74 if ((++(sdata->tries)) >= BBS_login_retry_times)
131 sysadm 1.55 {
132 sysadm 1.74 sdata->error = 1;
133 sysadm 1.55 }
134    
135     return SSH_AUTH_DENIED;
136     }
137    
138     static int pty_request(ssh_session session, ssh_channel channel, const char *term,
139 sysadm 1.74 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 sysadm 1.55 {
210 sysadm 1.74 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 sysadm 1.55 }
226    
227     static int shell_request(ssh_session session, ssh_channel channel, void *userdata)
228     {
229 sysadm 1.74 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 sysadm 1.55 }
246    
247 sysadm 1.74 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 sysadm 1.55
262 sysadm 1.74 static ssh_channel channel_open(ssh_session session, void *userdata)
263 sysadm 1.55 {
264 sysadm 1.74 (void)userdata;
265    
266 sysadm 1.55 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 sysadm 1.56 static int fork_server(void)
277 sysadm 1.55 {
278     ssh_event event;
279 sysadm 1.71 long int ssh_timeout = 0;
280 sysadm 1.55 int pid;
281     int i;
282     int ret;
283    
284 sysadm 1.74 /* 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 sysadm 1.55 .tries = 0,
304     .error = 0,
305     };
306    
307 sysadm 1.74 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 sysadm 1.55 .userdata = &cb_data,
317     .auth_password_function = auth_password,
318 sysadm 1.74 .channel_open_request_session_function = channel_open,
319 sysadm 1.55 };
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 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
337 sysadm 1.87 if (close(epollfd_server) < 0)
338     {
339     log_error("close(epollfd_server) error (%d)\n");
340     }
341 sysadm 1.88 #endif
342 sysadm 1.55
343 sysadm 1.87 for (i = 0; i < 2; i++)
344 sysadm 1.55 {
345 sysadm 1.87 if (close(socket_server[i]) == -1)
346     {
347     log_error("Close server socket failed\n");
348     }
349 sysadm 1.55 }
350    
351 sysadm 1.87 hash_dict_destroy(hash_dict_pid_sockaddr);
352     hash_dict_destroy(hash_dict_sockaddr_count);
353    
354 sysadm 1.55 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 sysadm 1.71 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 sysadm 1.75 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 sysadm 1.55 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 sysadm 1.58 for (i = 0; i < SSH_AUTH_MAX_DURATION && !SYS_server_exit && !cb_data.error && SSH_channel == NULL; i += 100)
390 sysadm 1.55 {
391 sysadm 1.58 ret = ssh_event_dopoll(event, 100); // 0.1 second
392 sysadm 1.55 if (ret == SSH_ERROR)
393     {
394 sysadm 1.73 #ifdef _DEBUG
395 sysadm 1.55 log_error("ssh_event_dopoll() error: %s\n", ssh_get_error(SSH_session));
396 sysadm 1.73 #endif
397 sysadm 1.55 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 sysadm 1.71
407 sysadm 1.74 ssh_set_channel_callbacks(SSH_channel, &channel_cb);
408    
409 sysadm 1.75 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 sysadm 1.71 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 sysadm 1.98
432     ssh_set_blocking(SSH_session, 0);
433 sysadm 1.55 }
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 sysadm 1.87 if (io_init() < 0)
450     {
451     log_error("io_init() error\n");
452     goto cleanup;
453     }
454    
455 sysadm 1.55 SYS_child_process_count = 0;
456    
457 sysadm 1.96 // BWF compile
458     if (bwf_compile() < 0)
459 sysadm 1.95 {
460 sysadm 1.96 log_error("bwf_compile() error\n");
461 sysadm 1.95 goto cleanup;
462     }
463    
464 sysadm 1.55 bbs_main();
465    
466     cleanup:
467     // Child process exit
468     SYS_server_exit = 1;
469    
470     if (SSH_v2)
471     {
472 sysadm 1.87 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 sysadm 1.74
489 sysadm 1.55 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 sysadm 1.96 // BWF cleanup
501     bwf_cleanup();
502 sysadm 1.95
503 sysadm 1.55 // Close Input and Output for client
504 sysadm 1.87 io_cleanup();
505 sysadm 1.55 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 sysadm 1.52 int net_server(const char *hostaddr, in_port_t port[])
517 sysadm 1.1 {
518 sysadm 1.100 struct stat file_stat;
519 sysadm 1.52 unsigned int addrlen;
520 sysadm 1.19 int ret;
521 sysadm 1.87 int flags_server[2];
522 sysadm 1.13 struct sockaddr_in sin;
523 sysadm 1.88
524     #ifdef HAVE_SYS_EPOLL_H
525 sysadm 1.25 struct epoll_event ev, events[MAX_EVENTS];
526 sysadm 1.88 #else
527     struct pollfd pfds[2];
528     #endif
529    
530 sysadm 1.87 int nfds;
531 sysadm 1.70 int notify_child_exit = 0;
532     time_t tm_notify_child_exit = time(NULL);
533 sysadm 1.44 int i, j;
534     pid_t pid;
535 sysadm 1.97 int ssh_key_valid = 0;
536 sysadm 1.51 int ssh_log_level = SSH_LOG_NOLOG;
537 sysadm 1.88
538 sysadm 1.86 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
539     int sd_notify_stopping = 0;
540     #endif
541 sysadm 1.10
542 sysadm 1.51 ssh_init();
543    
544     sshbind = ssh_bind_new();
545    
546 sysadm 1.97 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_RSA_KEY_FILE) < 0)
547     {
548 sysadm 1.99 log_error("Error loading SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE);
549 sysadm 1.97 }
550     else
551     {
552     ssh_key_valid = 1;
553     }
554     if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ED25519_KEY_FILE) < 0)
555     {
556 sysadm 1.99 log_error("Error loading SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE);
557     }
558     else
559     {
560     ssh_key_valid = 1;
561     }
562     if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ECDSA_KEY_FILE) < 0)
563     {
564     log_error("Error loading SSH ECDSA key: %s\n", SSH_HOST_ECDSA_KEY_FILE);
565 sysadm 1.97 }
566     else
567     {
568     ssh_key_valid = 1;
569     }
570    
571     if (!ssh_key_valid)
572     {
573     log_error("Error: no valid SSH host key\n");
574     ssh_bind_free(sshbind);
575     return -1;
576     }
577    
578 sysadm 1.51 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 ||
579     ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 ||
580 sysadm 1.99 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "+ssh-rsa") < 0 ||
581 sysadm 1.51 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0)
582     {
583     log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind));
584     ssh_bind_free(sshbind);
585     return -1;
586     }
587 sysadm 1.10
588 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
589 sysadm 1.87 epollfd_server = epoll_create1(0);
590     if (epollfd_server == -1)
591 sysadm 1.13 {
592 sysadm 1.52 log_error("epoll_create1() error (%d)\n", errno);
593 sysadm 1.27 return -1;
594 sysadm 1.13 }
595 sysadm 1.88 #endif
596 sysadm 1.10
597 sysadm 1.52 // Server socket
598     for (i = 0; i < 2; i++)
599 sysadm 1.23 {
600 sysadm 1.52 socket_server[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
601 sysadm 1.23
602 sysadm 1.52 if (socket_server[i] < 0)
603     {
604     log_error("Create socket_server error (%d)\n", errno);
605     return -1;
606     }
607 sysadm 1.10
608 sysadm 1.52 sin.sin_family = AF_INET;
609     sin.sin_addr.s_addr = (hostaddr[0] != '\0' ? inet_addr(hostaddr) : INADDR_ANY);
610     sin.sin_port = htons(port[i]);
611    
612     // Reuse address and port
613 sysadm 1.87 flags_server[i] = 1;
614     if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEADDR, &flags_server[i], sizeof(flags_server[i])) < 0)
615 sysadm 1.52 {
616     log_error("setsockopt SO_REUSEADDR error (%d)\n", errno);
617     }
618 sysadm 1.90 #if defined(SO_REUSEPORT)
619 sysadm 1.87 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEPORT, &flags_server[i], sizeof(flags_server[i])) < 0)
620 sysadm 1.52 {
621     log_error("setsockopt SO_REUSEPORT error (%d)\n", errno);
622     }
623 sysadm 1.90 #endif
624 sysadm 1.1
625 sysadm 1.52 if (bind(socket_server[i], (struct sockaddr *)&sin, sizeof(sin)) < 0)
626     {
627     log_error("Bind address %s:%u error (%d)\n",
628     inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), errno);
629     return -1;
630     }
631 sysadm 1.16
632 sysadm 1.52 if (listen(socket_server[i], 10) < 0)
633     {
634     log_error("Telnet socket listen error (%d)\n", errno);
635     return -1;
636     }
637 sysadm 1.13
638 sysadm 1.52 log_common("Listening at %s:%u\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
639 sysadm 1.25
640 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
641 sysadm 1.52 ev.events = EPOLLIN;
642     ev.data.fd = socket_server[i];
643 sysadm 1.87 if (epoll_ctl(epollfd_server, EPOLL_CTL_ADD, socket_server[i], &ev) == -1)
644 sysadm 1.30 {
645 sysadm 1.52 log_error("epoll_ctl(socket_server[%d]) error (%d)\n", i, errno);
646 sysadm 1.87 if (close(epollfd_server) < 0)
647 sysadm 1.52 {
648     log_error("close(epoll) error (%d)\n");
649     }
650     return -1;
651 sysadm 1.30 }
652 sysadm 1.88 #endif
653 sysadm 1.52
654 sysadm 1.87 flags_server[i] = fcntl(socket_server[i], F_GETFL, 0);
655     fcntl(socket_server[i], F_SETFL, flags_server[i] | O_NONBLOCK);
656 sysadm 1.25 }
657    
658 sysadm 1.98 ssh_bind_set_blocking(sshbind, 0);
659    
660 sysadm 1.82 hash_dict_pid_sockaddr = hash_dict_create(MAX_CLIENT_LIMIT);
661     if (hash_dict_pid_sockaddr == NULL)
662     {
663     log_error("hash_dict_create(hash_dict_pid_sockaddr) error\n");
664     return -1;
665     }
666     hash_dict_sockaddr_count = hash_dict_create(MAX_CLIENT_LIMIT);
667     if (hash_dict_sockaddr_count == NULL)
668     {
669     log_error("hash_dict_create(hash_dict_sockaddr_count) error\n");
670     return -1;
671     }
672    
673 sysadm 1.31 // Startup complete
674 sysadm 1.86 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
675 sysadm 1.31 sd_notifyf(0, "READY=1\n"
676 sysadm 1.52 "STATUS=Listening at %s:%d (Telnet) and %s:%d (SSH2)\n"
677 sysadm 1.31 "MAINPID=%d",
678 sysadm 1.52 hostaddr, port[0], hostaddr, port[1], getpid());
679 sysadm 1.86 #endif
680 sysadm 1.31
681 sysadm 1.19 while (!SYS_server_exit || SYS_child_process_count > 0)
682 sysadm 1.13 {
683 sysadm 1.86 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
684 sysadm 1.31 if (SYS_server_exit && !sd_notify_stopping)
685     {
686     sd_notify(0, "STOPPING=1");
687     sd_notify_stopping = 1;
688     }
689 sysadm 1.86 #endif
690 sysadm 1.37
691 sysadm 1.22 while ((SYS_child_exit || SYS_server_exit) && SYS_child_process_count > 0)
692 sysadm 1.19 {
693 sysadm 1.30 SYS_child_exit = 0;
694    
695 sysadm 1.92 pid = waitpid(-1, &ret, WNOHANG);
696 sysadm 1.89 if (pid > 0)
697 sysadm 1.19 {
698 sysadm 1.30 SYS_child_exit = 1; // Retry waitid
699 sysadm 1.92 SYS_child_process_count--;
700 sysadm 1.30
701 sysadm 1.92 if (WIFEXITED(ret))
702     {
703     log_common("Child process (%d) exited, status=%d\n", pid, WEXITSTATUS(ret));
704     }
705     else if (WIFSIGNALED(ret))
706     {
707     log_common("Child process (%d) is killed, status=%d\n", pid, WTERMSIG(ret));
708     }
709     else
710     {
711     log_common("Child process (%d) exited abnormally, status=%d\n", pid, ret);
712     }
713 sysadm 1.44
714 sysadm 1.89 if (pid != section_list_loader_pid)
715 sysadm 1.44 {
716 sysadm 1.82 j = 0;
717 sysadm 1.89 ret = hash_dict_get(hash_dict_pid_sockaddr, (uint64_t)pid, (int64_t *)&j);
718 sysadm 1.82 if (ret < 0)
719     {
720 sysadm 1.89 log_error("hash_dict_get(hash_dict_pid_sockaddr, %d) error\n", pid);
721 sysadm 1.82 }
722     else
723 sysadm 1.44 {
724 sysadm 1.85 ret = hash_dict_inc(hash_dict_sockaddr_count, (uint64_t)j, -1);
725 sysadm 1.106 if (ret <= 0)
726 sysadm 1.82 {
727 sysadm 1.106 log_error("hash_dict_inc(hash_dict_sockaddr_count, %d, -1) error: %d\n", j, ret);
728 sysadm 1.82 }
729    
730 sysadm 1.89 ret = hash_dict_del(hash_dict_pid_sockaddr, (uint64_t)pid);
731 sysadm 1.82 if (ret < 0)
732 sysadm 1.45 {
733 sysadm 1.89 log_error("hash_dict_del(hash_dict_pid_sockaddr, %d) error\n", pid);
734 sysadm 1.45 }
735     }
736 sysadm 1.44 }
737 sysadm 1.19 }
738 sysadm 1.89 else if (pid == 0)
739 sysadm 1.19 {
740     break;
741     }
742 sysadm 1.89 else if (pid < 0)
743 sysadm 1.19 {
744 sysadm 1.89 log_error("Error in waitpid(): %d\n", errno);
745 sysadm 1.19 break;
746     }
747     }
748    
749 sysadm 1.22 if (SYS_server_exit && !SYS_child_exit && SYS_child_process_count > 0)
750 sysadm 1.21 {
751 sysadm 1.70 if (notify_child_exit == 0)
752 sysadm 1.21 {
753 sysadm 1.86 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
754 sysadm 1.70 sd_notifyf(0, "STATUS=Notify %d child process to exit", SYS_child_process_count);
755     log_common("Notify %d child process to exit\n", SYS_child_process_count);
756 sysadm 1.86 #endif
757 sysadm 1.70
758 sysadm 1.93 if (kill(0, SIGTERM) < 0)
759 sysadm 1.70 {
760     log_error("Send SIGTERM signal failed (%d)\n", errno);
761     }
762    
763     notify_child_exit = 1;
764     tm_notify_child_exit = time(NULL);
765 sysadm 1.21 }
766 sysadm 1.70 else if (notify_child_exit == 1 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_EXIT_TIMEOUT)
767     {
768 sysadm 1.86 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
769 sysadm 1.70 sd_notifyf(0, "STATUS=Kill %d child process", SYS_child_process_count);
770 sysadm 1.86 #endif
771 sysadm 1.31
772 sysadm 1.93 if (kill(0, SIGKILL) < 0)
773 sysadm 1.70 {
774 sysadm 1.82 log_error("Send SIGKILL signal failed (%d)\n", errno);
775 sysadm 1.70 }
776 sysadm 1.69
777 sysadm 1.70 notify_child_exit = 2;
778     tm_notify_child_exit = time(NULL);
779     }
780     else if (notify_child_exit == 2 && time(NULL) - tm_notify_child_exit >= WAIT_CHILD_PROCESS_KILL_TIMEOUT)
781     {
782     log_error("Main process prepare to exit without waiting for %d child process any longer\n", SYS_child_process_count);
783     SYS_child_process_count = 0;
784     }
785 sysadm 1.21 }
786    
787 sysadm 1.44 if (SYS_conf_reload && !SYS_server_exit)
788 sysadm 1.19 {
789 sysadm 1.44 SYS_conf_reload = 0;
790 sysadm 1.86
791     #ifdef HAVE_SYSTEMD_SD_DAEMON_H
792 sysadm 1.37 sd_notify(0, "RELOADING=1");
793 sysadm 1.86 #endif
794 sysadm 1.30
795 sysadm 1.84 // Restart log
796     if (log_restart() < 0)
797     {
798     log_error("Restart logging failed\n");
799     }
800    
801 sysadm 1.44 // Reload configuration
802     if (load_conf(CONF_BBSD) < 0)
803     {
804     log_error("Reload conf failed\n");
805     }
806    
807 sysadm 1.80 // Reload BWF config
808     if (bwf_load(CONF_BWF) < 0)
809     {
810     log_error("Reload BWF conf failed\n");
811     }
812    
813 sysadm 1.100 // Get EULA modification tm
814     if (stat(DATA_EULA, &file_stat) == -1)
815     {
816     log_error("stat(%s) error\n", DATA_EULA, errno);
817     }
818     else
819     {
820     BBS_eula_tm = file_stat.st_mtim.tv_sec;
821     }
822    
823 sysadm 1.94 if (detach_menu_shm(&bbs_menu) < 0)
824 sysadm 1.34 {
825 sysadm 1.94 log_error("detach_menu_shm(bbs_menu) error\n");
826 sysadm 1.19 }
827 sysadm 1.94 if (load_menu(&bbs_menu, CONF_MENU) < 0)
828 sysadm 1.19 {
829 sysadm 1.94 log_error("load_menu(bbs_menu) error\n");
830 sysadm 1.68 unload_menu(&bbs_menu);
831 sysadm 1.72 }
832    
833 sysadm 1.94 if (detach_menu_shm(&top10_menu) < 0)
834 sysadm 1.72 {
835 sysadm 1.94 log_error("detach_menu_shm(top10_menu) error\n");
836 sysadm 1.72 }
837 sysadm 1.94 if (load_menu(&top10_menu, CONF_TOP10_MENU) < 0)
838 sysadm 1.72 {
839 sysadm 1.94 log_error("load_menu(top10_menu) error\n");
840 sysadm 1.72 unload_menu(&top10_menu);
841 sysadm 1.19 }
842 sysadm 1.102 top10_menu.allow_exit = 1;
843 sysadm 1.37
844 sysadm 1.35 for (int i = 0; i < data_files_load_startup_count; i++)
845     {
846 sysadm 1.39 if (load_file(data_files_load_startup[i]) < 0)
847 sysadm 1.35 {
848 sysadm 1.76 log_error("load_file(%s) error\n", data_files_load_startup[i]);
849 sysadm 1.35 }
850     }
851    
852 sysadm 1.68 // Load section config and gen_ex
853     if (load_section_config_from_db(1) < 0)
854     {
855     log_error("load_section_config_from_db(1) error\n");
856     }
857    
858 sysadm 1.84 // Notify child processes to reload configuration
859 sysadm 1.93 if (kill(0, SIGUSR1) < 0)
860 sysadm 1.84 {
861     log_error("Send SIGUSR1 signal failed (%d)\n", errno);
862     }
863    
864 sysadm 1.86 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
865 sysadm 1.68 sd_notify(0, "READY=1");
866 sysadm 1.86 #endif
867 sysadm 1.42 }
868    
869 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
870 sysadm 1.87 nfds = epoll_wait(epollfd_server, events, MAX_EVENTS, 100); // 0.1 second
871 sysadm 1.88 ret = nfds;
872     #else
873     pfds[0].fd = socket_server[0];
874     pfds[0].events = POLLIN;
875     pfds[1].fd = socket_server[1];
876     pfds[1].events = POLLIN;
877     nfds = 2;
878     ret = poll(pfds, (nfds_t)nfds, 100); // 0.1 second
879     #endif
880     if (ret < 0)
881 sysadm 1.19 {
882     if (errno != EINTR)
883     {
884 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
885 sysadm 1.25 log_error("epoll_wait() error (%d)\n", errno);
886 sysadm 1.88 #else
887     log_error("poll() error (%d)\n", errno);
888     #endif
889 sysadm 1.25 break;
890 sysadm 1.19 }
891     continue;
892     }
893 sysadm 1.13
894 sysadm 1.19 // Stop accept new connection on exit
895     if (SYS_server_exit)
896 sysadm 1.13 {
897     continue;
898     }
899    
900 sysadm 1.25 for (int i = 0; i < nfds; i++)
901 sysadm 1.13 {
902 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
903 sysadm 1.52 if (events[i].data.fd == socket_server[0] || events[i].data.fd == socket_server[1])
904 sysadm 1.88 #else
905     if ((pfds[i].fd == socket_server[0] || pfds[i].fd == socket_server[1]) && (pfds[i].revents & POLLIN))
906     #endif
907 sysadm 1.13 {
908 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
909 sysadm 1.53 SSH_v2 = (events[i].data.fd == socket_server[1] ? 1 : 0);
910 sysadm 1.88 #else
911     SSH_v2 = (pfds[i].fd == socket_server[1] ? 1 : 0);
912     #endif
913 sysadm 1.52
914 sysadm 1.29 while (!SYS_server_exit) // Accept all incoming connections until error
915 sysadm 1.13 {
916 sysadm 1.52 addrlen = sizeof(sin);
917 sysadm 1.91 socket_client = accept(socket_server[SSH_v2], (struct sockaddr *)&sin, (socklen_t *)&addrlen);
918 sysadm 1.25 if (socket_client < 0)
919     {
920 sysadm 1.28 if (errno == EAGAIN || errno == EWOULDBLOCK)
921     {
922     break;
923     }
924     else if (errno == EINTR)
925     {
926     continue;
927     }
928     else
929 sysadm 1.25 {
930     log_error("accept(socket_server) error (%d)\n", errno);
931 sysadm 1.28 break;
932 sysadm 1.25 }
933     }
934    
935     strncpy(hostaddr_client, inet_ntoa(sin.sin_addr), sizeof(hostaddr_client) - 1);
936     hostaddr_client[sizeof(hostaddr_client) - 1] = '\0';
937    
938     port_client = ntohs(sin.sin_port);
939    
940 sysadm 1.43 if (SYS_child_process_count - 1 < BBS_max_client)
941 sysadm 1.25 {
942 sysadm 1.44 j = 0;
943 sysadm 1.82 ret = hash_dict_get(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, (int64_t *)&j);
944     if (ret < 0)
945 sysadm 1.44 {
946 sysadm 1.82 log_error("hash_dict_get(hash_dict_sockaddr_count, %s) error\n", hostaddr_client);
947 sysadm 1.44 }
948    
949     if (j < BBS_max_client_per_ip)
950 sysadm 1.43 {
951 sysadm 1.52 if ((pid = fork_server()) < 0)
952 sysadm 1.44 {
953     log_error("fork_server() error\n");
954     }
955     else if (pid > 0)
956     {
957 sysadm 1.82 ret = hash_dict_set(hash_dict_pid_sockaddr, (uint64_t)pid, sin.sin_addr.s_addr);
958     if (ret < 0)
959 sysadm 1.44 {
960 sysadm 1.82 log_error("hash_dict_set(hash_dict_pid_sockaddr, %d, %s) error\n", pid, hostaddr_client);
961 sysadm 1.44 }
962    
963 sysadm 1.106 if (j == 0)
964     {
965     // First connection from this IP
966     log_common("Accept %s connection from %s:%d\n",
967     (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client);
968    
969     ret = hash_dict_set(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, 1);
970     if (ret < 0)
971     {
972     log_error("hash_dict_set(hash_dict_sockaddr_count, %s, 1) error\n", hostaddr_client);
973     }
974     }
975     else
976 sysadm 1.44 {
977 sysadm 1.106 // Increase connection count from this IP
978     log_common("Accept %s connection from %s:%d, already have %d connections\n",
979     (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, j);
980    
981     ret = hash_dict_inc(hash_dict_sockaddr_count, (uint64_t)sin.sin_addr.s_addr, 1);
982     if (ret <= 0)
983     {
984     log_error("hash_dict_inc(hash_dict_sockaddr_count, %s, 1) error: %d\n", hostaddr_client, ret);
985     }
986 sysadm 1.44 }
987     }
988 sysadm 1.43 }
989 sysadm 1.82 else
990     {
991 sysadm 1.105 log_error("Rejected %s connection from %s:%d over limit per IP (%d >= %d)\n",
992     (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, j, BBS_max_client_per_ip);
993 sysadm 1.82 }
994 sysadm 1.43 }
995     else
996     {
997 sysadm 1.105 log_error("Rejected %s connection from %s:%d over limit (%d >= %d)\n",
998     (SSH_v2 ? "SSH" : "telnet"), hostaddr_client, port_client, SYS_child_process_count - 1, BBS_max_client);
999 sysadm 1.25 }
1000    
1001     if (close(socket_client) == -1)
1002     {
1003     log_error("close(socket_lient) error (%d)\n", errno);
1004     }
1005 sysadm 1.13 }
1006     }
1007     }
1008 sysadm 1.25 }
1009 sysadm 1.13
1010 sysadm 1.88 #ifdef HAVE_SYS_EPOLL_H
1011 sysadm 1.87 if (close(epollfd_server) < 0)
1012 sysadm 1.30 {
1013 sysadm 1.87 log_error("close(epollfd_server) error (%d)\n");
1014 sysadm 1.30 }
1015 sysadm 1.88 #endif
1016 sysadm 1.30
1017 sysadm 1.52 for (i = 0; i < 2; i++)
1018     {
1019     if (close(socket_server[i]) == -1)
1020     {
1021     log_error("Close server socket failed\n");
1022     }
1023 sysadm 1.13 }
1024 sysadm 1.7
1025 sysadm 1.82 hash_dict_destroy(hash_dict_pid_sockaddr);
1026     hash_dict_destroy(hash_dict_sockaddr_count);
1027    
1028 sysadm 1.51 ssh_bind_free(sshbind);
1029     ssh_finalize();
1030    
1031 sysadm 1.13 return 0;
1032 sysadm 1.1 }

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