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

Contents of /lbbs/src/net_server.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.53 - (show annotations)
Thu Jun 5 09:04:55 2025 UTC (9 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.52: +2 -2 lines
Content type: text/x-csrc
Fix bug

1 /***************************************************************************
2 net_server.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #define _XOPEN_SOURCE 500
18 #define _POSIX_C_SOURCE 200809L
19 #define _GNU_SOURCE
20
21 #include "net_server.h"
22 #include "common.h"
23 #include "log.h"
24 #include "io.h"
25 #include "init.h"
26 #include "fork.h"
27 #include "menu.h"
28 #include "file_loader.h"
29 #include "section_list_loader.h"
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <sys/syscall.h>
37 #include <sys/socket.h>
38 #include <sys/wait.h>
39 #include <sys/epoll.h>
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 #include <systemd/sd-daemon.h>
43
44 struct process_sockaddr_t
45 {
46 pid_t pid;
47 in_addr_t s_addr;
48 };
49 typedef struct process_sockaddr_t PROCESS_SOCKADDR;
50
51 static PROCESS_SOCKADDR process_sockaddr_pool[MAX_CLIENT_LIMIT];
52
53 int net_server(const char *hostaddr, in_port_t port[])
54 {
55 unsigned int addrlen;
56 int ret;
57 int flags[2];
58 struct sockaddr_in sin;
59 struct epoll_event ev, events[MAX_EVENTS];
60 int nfds, epollfd;
61 siginfo_t siginfo;
62 int sd_notify_stopping = 0;
63 MENU_SET *p_bbs_menu_new;
64 int i, j;
65 pid_t pid;
66 int ssh_log_level = SSH_LOG_NOLOG;
67
68 ssh_init();
69
70 sshbind = ssh_bind_new();
71
72 if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 ||
73 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 ||
74 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_KEYFILE) < 0 ||
75 ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0)
76 {
77 log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind));
78 ssh_bind_free(sshbind);
79 return -1;
80 }
81
82 epollfd = epoll_create1(0);
83 if (epollfd < 0)
84 {
85 log_error("epoll_create1() error (%d)\n", errno);
86 return -1;
87 }
88
89 // Server socket
90 for (i = 0; i < 2; i++)
91 {
92 socket_server[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
93
94 if (socket_server[i] < 0)
95 {
96 log_error("Create socket_server error (%d)\n", errno);
97 return -1;
98 }
99
100 sin.sin_family = AF_INET;
101 sin.sin_addr.s_addr = (hostaddr[0] != '\0' ? inet_addr(hostaddr) : INADDR_ANY);
102 sin.sin_port = htons(port[i]);
103
104 // Reuse address and port
105 flags[i] = 1;
106 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEADDR, &flags[i], sizeof(flags[i])) < 0)
107 {
108 log_error("setsockopt SO_REUSEADDR error (%d)\n", errno);
109 }
110 if (setsockopt(socket_server[i], SOL_SOCKET, SO_REUSEPORT, &flags[i], sizeof(flags[i])) < 0)
111 {
112 log_error("setsockopt SO_REUSEPORT error (%d)\n", errno);
113 }
114
115 if (bind(socket_server[i], (struct sockaddr *)&sin, sizeof(sin)) < 0)
116 {
117 log_error("Bind address %s:%u error (%d)\n",
118 inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), errno);
119 return -1;
120 }
121
122 if (listen(socket_server[i], 10) < 0)
123 {
124 log_error("Telnet socket listen error (%d)\n", errno);
125 return -1;
126 }
127
128 log_common("Listening at %s:%u\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
129
130 ev.events = EPOLLIN;
131 ev.data.fd = socket_server[i];
132 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, socket_server[i], &ev) == -1)
133 {
134 log_error("epoll_ctl(socket_server[%d]) error (%d)\n", i, errno);
135 if (close(epollfd) < 0)
136 {
137 log_error("close(epoll) error (%d)\n");
138 }
139 return -1;
140 }
141
142 flags[i] = fcntl(socket_server[i], F_GETFL, 0);
143 fcntl(socket_server[i], F_SETFL, flags[i] | O_NONBLOCK);
144 }
145
146 // Startup complete
147 sd_notifyf(0, "READY=1\n"
148 "STATUS=Listening at %s:%d (Telnet) and %s:%d (SSH2)\n"
149 "MAINPID=%d",
150 hostaddr, port[0], hostaddr, port[1], getpid());
151
152 while (!SYS_server_exit || SYS_child_process_count > 0)
153 {
154 if (SYS_server_exit && !sd_notify_stopping)
155 {
156 sd_notify(0, "STOPPING=1");
157 sd_notify_stopping = 1;
158 }
159
160 while ((SYS_child_exit || SYS_server_exit) && SYS_child_process_count > 0)
161 {
162 SYS_child_exit = 0;
163
164 siginfo.si_pid = 0;
165 ret = waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG);
166 if (ret == 0 && siginfo.si_pid > 0)
167 {
168 SYS_child_exit = 1; // Retry waitid
169
170 SYS_child_process_count--;
171 log_common("Child process (%d) exited\n", siginfo.si_pid);
172
173 if (siginfo.si_pid != section_list_loader_pid)
174 {
175 i = 0;
176 for (; i < BBS_max_client; i++)
177 {
178 if (process_sockaddr_pool[i].pid == siginfo.si_pid)
179 {
180 process_sockaddr_pool[i].pid = 0;
181 break;
182 }
183 }
184 if (i >= BBS_max_client)
185 {
186 log_error("Child process (%d) not found in process sockaddr pool\n", siginfo.si_pid);
187 }
188 }
189 }
190 else if (ret == 0)
191 {
192 break;
193 }
194 else if (ret < 0)
195 {
196 log_error("Error in waitid: %d\n", errno);
197 break;
198 }
199 }
200
201 if (SYS_server_exit && !SYS_child_exit && SYS_child_process_count > 0)
202 {
203 log_common("Notify %d child process to exit\n", SYS_child_process_count);
204 if (kill(0, SIGTERM) < 0)
205 {
206 log_error("Send SIGTERM signal failed (%d)\n", errno);
207 }
208
209 sd_notifyf(0, "STATUS=Waiting for %d child process to exit", SYS_child_process_count);
210 }
211
212 if (SYS_conf_reload && !SYS_server_exit)
213 {
214 SYS_conf_reload = 0;
215 sd_notify(0, "RELOADING=1");
216
217 // Reload configuration
218 if (load_conf(CONF_BBSD) < 0)
219 {
220 log_error("Reload conf failed\n");
221 }
222
223 p_bbs_menu_new = calloc(1, sizeof(MENU_SET));
224 if (p_bbs_menu_new == NULL)
225 {
226 log_error("OOM: calloc(MENU_SET)\n");
227 }
228 else if (load_menu(p_bbs_menu_new, CONF_MENU) < 0)
229 {
230 unload_menu(p_bbs_menu_new);
231 free(p_bbs_menu_new);
232 p_bbs_menu_new = NULL;
233
234 log_error("Reload menu failed\n");
235 }
236 else
237 {
238 unload_menu(p_bbs_menu);
239 free(p_bbs_menu);
240
241 p_bbs_menu = p_bbs_menu_new;
242 p_bbs_menu_new = NULL;
243
244 log_common("Reload menu successfully\n");
245 }
246
247 sd_notify(0, "READY=1");
248 }
249
250 if (SYS_data_file_reload && !SYS_server_exit)
251 {
252 SYS_data_file_reload = 0;
253 sd_notify(0, "RELOADING=1");
254
255 for (int i = 0; i < data_files_load_startup_count; i++)
256 {
257 if (load_file(data_files_load_startup[i]) < 0)
258 {
259 log_error("load_file_mmap(%s) error\n", data_files_load_startup[i]);
260 }
261 }
262
263 log_common("Reload data files successfully\n");
264 sd_notify(0, "READY=1");
265 }
266
267 if (SYS_section_list_reload && !SYS_server_exit)
268 {
269 SYS_section_list_reload = 0;
270
271 if (section_list_loader_reload() < 0)
272 {
273 log_error("ksection_list_loader_reload() failed\n");
274 }
275 }
276
277 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
278
279 if (nfds < 0)
280 {
281 if (errno != EINTR)
282 {
283 log_error("epoll_wait() error (%d)\n", errno);
284 break;
285 }
286 continue;
287 }
288
289 // Stop accept new connection on exit
290 if (SYS_server_exit)
291 {
292 continue;
293 }
294
295 for (int i = 0; i < nfds; i++)
296 {
297 if (events[i].data.fd == socket_server[0] || events[i].data.fd == socket_server[1])
298 {
299 SSH_v2 = (events[i].data.fd == socket_server[1] ? 1 : 0);
300
301 while (!SYS_server_exit) // Accept all incoming connections until error
302 {
303 addrlen = sizeof(sin);
304 socket_client = accept(socket_server[SSH_v2], (struct sockaddr *)&sin, &addrlen);
305 if (socket_client < 0)
306 {
307 if (errno == EAGAIN || errno == EWOULDBLOCK)
308 {
309 break;
310 }
311 else if (errno == EINTR)
312 {
313 continue;
314 }
315 else
316 {
317 log_error("accept(socket_server) error (%d)\n", errno);
318 break;
319 }
320 }
321
322 strncpy(hostaddr_client, inet_ntoa(sin.sin_addr), sizeof(hostaddr_client) - 1);
323 hostaddr_client[sizeof(hostaddr_client) - 1] = '\0';
324
325 port_client = ntohs(sin.sin_port);
326
327 log_common("Accept connection from %s:%d\n", hostaddr_client, port_client);
328
329 if (SYS_child_process_count - 1 < BBS_max_client)
330 {
331 j = 0;
332 for (i = 0; i < BBS_max_client; i++)
333 {
334 if (process_sockaddr_pool[i].pid != 0 && process_sockaddr_pool[i].s_addr == sin.sin_addr.s_addr)
335 {
336 j++;
337 if (j >= BBS_max_client_per_ip)
338 {
339 log_common("Too many client connections (%d) from %s\n", j, hostaddr_client);
340 break;
341 }
342 }
343 }
344
345 if (j < BBS_max_client_per_ip)
346 {
347 if ((pid = fork_server()) < 0)
348 {
349 log_error("fork_server() error\n");
350 }
351 else if (pid > 0)
352 {
353 i = 0;
354 for (; i < BBS_max_client; i++)
355 {
356 if (process_sockaddr_pool[i].pid == 0)
357 {
358 break;
359 }
360 }
361
362 if (i >= BBS_max_client)
363 {
364 log_error("Process sockaddr pool depleted\n");
365 }
366 else
367 {
368 process_sockaddr_pool[i].pid = pid;
369 process_sockaddr_pool[i].s_addr = sin.sin_addr.s_addr;
370 }
371 }
372 }
373 }
374 else
375 {
376 log_error("Rejected client connection over limit (%d)\n", SYS_child_process_count - 1);
377 }
378
379 if (close(socket_client) == -1)
380 {
381 log_error("close(socket_lient) error (%d)\n", errno);
382 }
383 }
384 }
385 }
386 }
387
388 if (close(epollfd) < 0)
389 {
390 log_error("close(epoll) error (%d)\n");
391 }
392
393 for (i = 0; i < 2; i++)
394 {
395 fcntl(socket_server[i], F_SETFL, flags[i]);
396
397 if (close(socket_server[i]) == -1)
398 {
399 log_error("Close server socket failed\n");
400 }
401 }
402
403 ssh_bind_free(sshbind);
404 ssh_finalize();
405
406 return 0;
407 }

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