/[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.100 - (hide annotations)
Tue Dec 2 08:04:29 2025 UTC (3 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.99: +12 -0 lines
Content type: text/x-csrc
Check update time of EULA while user login

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

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