/[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.112 - (hide annotations)
Fri Dec 19 06:16:27 2025 UTC (2 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.111: +74 -74 lines
Content type: text/x-csrc
Append \n to the end of logging message by log_...()
Remove ending \n from each logging message

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

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