BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file dostest-server.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29  
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34  
35 #ifdef BADVPN_LINUX
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #endif
39  
40 #include <misc/debug.h>
41 #include <misc/version.h>
42 #include <misc/offset.h>
43 #include <misc/open_standard_streams.h>
44 #include <misc/balloc.h>
45 #include <misc/loglevel.h>
46 #include <structure/LinkedList1.h>
47 #include <base/BLog.h>
48 #include <system/BAddr.h>
49 #include <system/BReactor.h>
50 #include <system/BNetwork.h>
51 #include <system/BConnection.h>
52 #include <system/BSignal.h>
53 #include "StreamBuffer.h"
54  
55 #include <generated/blog_channel_dostest_server.h>
56  
57 #define PROGRAM_NAME "dostest-server"
58  
59 #ifdef BADVPN_LINUX
60 #ifndef SO_DOSDEF_PREPARE
61 #define SO_DOSDEF_PREPARE 48
62 #endif
63 #ifndef SO_DOSDEF_ACTIVATE
64 #define SO_DOSDEF_ACTIVATE 49
65 #endif
66 #endif
67  
68 #define BUF_SIZE 1024
69  
70 // client structure
71 struct client {
72 BConnection con;
73 BAddr addr;
74 StreamBuffer buf;
75 BTimer disconnect_timer;
76 LinkedList1Node clients_list_node;
77 };
78  
79 // command-line options
80 static struct {
81 int help;
82 int version;
83 char *listen_addr;
84 int max_clients;
85 int disconnect_time;
86 int defense_prepare_clients;
87 int defense_activate_clients;
88 int loglevel;
89 int loglevels[BLOG_NUM_CHANNELS];
90 } options;
91  
92 // listen address
93 static BAddr listen_addr;
94  
95 // reactor
96 static BReactor ss;
97  
98 // listener
99 static BListener listener;
100  
101 // clients
102 static LinkedList1 clients_list;
103 static int num_clients;
104  
105 // defense status
106 static int defense_prepare;
107 static int defense_activate;
108  
109 static void print_help (const char *name);
110 static void print_version (void);
111 static int parse_arguments (int argc, char *argv[]);
112 static int process_arguments (void);
113 static void signal_handler (void *unused);
114 static void listener_handler (void *unused);
115 static void client_free (struct client *client);
116 static void client_logfunc (struct client *client);
117 static void client_log (struct client *client, int level, const char *fmt, ...);
118 static void client_disconnect_timer_handler (struct client *client);
119 static void client_connection_handler (struct client *client, int event);
120 static void update_defense (void);
121  
122 int main (int argc, char **argv)
123 {
124 if (argc <= 0) {
125 return 1;
126 }
127  
128 // open standard streams
129 open_standard_streams();
130  
131 // parse command-line arguments
132 if (!parse_arguments(argc, argv)) {
133 fprintf(stderr, "Failed to parse arguments\n");
134 print_help(argv[0]);
135 goto fail0;
136 }
137  
138 // handle --help and --version
139 if (options.help) {
140 print_version();
141 print_help(argv[0]);
142 return 0;
143 }
144 if (options.version) {
145 print_version();
146 return 0;
147 }
148  
149 // init loger
150 BLog_InitStderr();
151  
152 // configure logger channels
153 for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
154 if (options.loglevels[i] >= 0) {
155 BLog_SetChannelLoglevel(i, options.loglevels[i]);
156 }
157 else if (options.loglevel >= 0) {
158 BLog_SetChannelLoglevel(i, options.loglevel);
159 }
160 }
161  
162 BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
163  
164 // initialize network
165 if (!BNetwork_GlobalInit()) {
166 BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
167 goto fail1;
168 }
169  
170 // process arguments
171 if (!process_arguments()) {
172 BLog(BLOG_ERROR, "Failed to process arguments");
173 goto fail1;
174 }
175  
176 // init time
177 BTime_Init();
178  
179 // init reactor
180 if (!BReactor_Init(&ss)) {
181 BLog(BLOG_ERROR, "BReactor_Init failed");
182 goto fail1;
183 }
184  
185 // setup signal handler
186 if (!BSignal_Init(&ss, signal_handler, NULL)) {
187 BLog(BLOG_ERROR, "BSignal_Init failed");
188 goto fail2;
189 }
190  
191 // initialize listener
192 if (!BListener_Init(&listener, listen_addr, &ss, NULL, listener_handler)) {
193 BLog(BLOG_ERROR, "Listener_Init failed");
194 goto fail3;
195 }
196  
197 // init clients list
198 LinkedList1_Init(&clients_list);
199 num_clients = 0;
200  
201 // clear defense state
202 defense_prepare = 0;
203 defense_activate = 0;
204  
205 // update defense
206 update_defense();
207  
208 // enter event loop
209 BLog(BLOG_NOTICE, "entering event loop");
210 BReactor_Exec(&ss);
211  
212 // free clients
213 while (!LinkedList1_IsEmpty(&clients_list)) {
214 struct client *client = UPPER_OBJECT(LinkedList1_GetFirst(&clients_list), struct client, clients_list_node);
215 client_free(client);
216 }
217 // free listener
218 BListener_Free(&listener);
219 fail3:
220 // free signal
221 BSignal_Finish();
222 fail2:
223 // free reactor
224 BReactor_Free(&ss);
225 fail1:
226 // free logger
227 BLog(BLOG_NOTICE, "exiting");
228 BLog_Free();
229 fail0:
230 // finish debug objects
231 DebugObjectGlobal_Finish();
232  
233 return 1;
234 }
235  
236 void print_help (const char *name)
237 {
238 printf(
239 "Usage:\n"
240 " %s\n"
241 " [--help]\n"
242 " [--version]\n"
243 " --listen-addr <addr>\n"
244 " --max-clients <number>\n"
245 " --disconnect-time <milliseconds>\n"
246 " [--defense-prepare-clients <number>]\n"
247 " [--defense-activate-clients <number>]\n"
248 " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n"
249 " [--channel-loglevel <channel-name> <0-5/none/error/warning/notice/info/debug>] ...\n"
250 "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n",
251 name
252 );
253 }
254  
255 void print_version (void)
256 {
257 printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n");
258 }
259  
260 int parse_arguments (int argc, char *argv[])
261 {
262 options.help = 0;
263 options.version = 0;
264 options.listen_addr = NULL;
265 options.max_clients = -1;
266 options.disconnect_time = -1;
267 options.defense_prepare_clients = -1;
268 options.defense_activate_clients = -1;
269 options.loglevel = -1;
270 for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
271 options.loglevels[i] = -1;
272 }
273  
274 int i;
275 for (i = 1; i < argc; i++) {
276 char *arg = argv[i];
277 if (!strcmp(arg, "--help")) {
278 options.help = 1;
279 }
280 else if (!strcmp(arg, "--version")) {
281 options.version = 1;
282 }
283 else if (!strcmp(arg, "--listen-addr")) {
284 if (1 >= argc - i) {
285 fprintf(stderr, "%s: requires an argument\n", arg);
286 return 0;
287 }
288 options.listen_addr = argv[i + 1];
289 i++;
290 }
291 else if (!strcmp(arg, "--max-clients")) {
292 if (1 >= argc - i) {
293 fprintf(stderr, "%s: requires an argument\n", arg);
294 return 0;
295 }
296 if ((options.max_clients = atoi(argv[i + 1])) <= 0) {
297 fprintf(stderr, "%s: wrong argument\n", arg);
298 return 0;
299 }
300 i++;
301 }
302 else if (!strcmp(arg, "--disconnect-time")) {
303 if (1 >= argc - i) {
304 fprintf(stderr, "%s: requires an argument\n", arg);
305 return 0;
306 }
307 if ((options.disconnect_time = atoi(argv[i + 1])) <= 0) {
308 fprintf(stderr, "%s: wrong argument\n", arg);
309 return 0;
310 }
311 i++;
312 }
313 else if (!strcmp(arg, "--defense-prepare-clients")) {
314 if (1 >= argc - i) {
315 fprintf(stderr, "%s: requires an argument\n", arg);
316 return 0;
317 }
318 if ((options.defense_prepare_clients = atoi(argv[i + 1])) <= 0) {
319 fprintf(stderr, "%s: wrong argument\n", arg);
320 return 0;
321 }
322 i++;
323 }
324 else if (!strcmp(arg, "--defense-activate-clients")) {
325 if (1 >= argc - i) {
326 fprintf(stderr, "%s: requires an argument\n", arg);
327 return 0;
328 }
329 if ((options.defense_activate_clients = atoi(argv[i + 1])) <= 0) {
330 fprintf(stderr, "%s: wrong argument\n", arg);
331 return 0;
332 }
333 i++;
334 }
335 else if (!strcmp(arg, "--loglevel")) {
336 if (1 >= argc - i) {
337 fprintf(stderr, "%s: requires an argument\n", arg);
338 return 0;
339 }
340 if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) {
341 fprintf(stderr, "%s: wrong argument\n", arg);
342 return 0;
343 }
344 i++;
345 }
346 else if (!strcmp(arg, "--channel-loglevel")) {
347 if (2 >= argc - i) {
348 fprintf(stderr, "%s: requires two arguments\n", arg);
349 return 0;
350 }
351 int channel = BLogGlobal_GetChannelByName(argv[i + 1]);
352 if (channel < 0) {
353 fprintf(stderr, "%s: wrong channel argument\n", arg);
354 return 0;
355 }
356 int loglevel = parse_loglevel(argv[i + 2]);
357 if (loglevel < 0) {
358 fprintf(stderr, "%s: wrong loglevel argument\n", arg);
359 return 0;
360 }
361 options.loglevels[channel] = loglevel;
362 i += 2;
363 }
364 else {
365 fprintf(stderr, "unknown option: %s\n", arg);
366 return 0;
367 }
368 }
369  
370 if (options.help || options.version) {
371 return 1;
372 }
373  
374 if (!options.listen_addr) {
375 fprintf(stderr, "--listen-addr missing\n");
376 return 0;
377 }
378  
379 if (options.max_clients == -1) {
380 fprintf(stderr, "--max-clients missing\n");
381 return 0;
382 }
383  
384 if (options.disconnect_time == -1) {
385 fprintf(stderr, "--disconnect-time missing\n");
386 return 0;
387 }
388  
389 return 1;
390 }
391  
392 int process_arguments (void)
393 {
394 // resolve listen address
395 if (!BAddr_Parse(&listen_addr, options.listen_addr, NULL, 0)) {
396 BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed");
397 return 0;
398 }
399  
400 return 1;
401 }
402  
403 void signal_handler (void *unused)
404 {
405 BLog(BLOG_NOTICE, "termination requested");
406  
407 // exit event loop
408 BReactor_Quit(&ss, 1);
409 }
410  
411 void listener_handler (void *unused)
412 {
413 if (num_clients == options.max_clients) {
414 BLog(BLOG_ERROR, "maximum number of clients reached");
415 goto fail0;
416 }
417  
418 // allocate structure
419 struct client *client = (struct client *)malloc(sizeof(*client));
420 if (!client) {
421 BLog(BLOG_ERROR, "malloc failed");
422 goto fail0;
423 }
424  
425 // accept client
426 if (!BConnection_Init(&client->con, BConnection_source_listener(&listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) {
427 BLog(BLOG_ERROR, "BConnection_Init failed");
428 goto fail1;
429 }
430  
431 // init connection interfaces
432 BConnection_RecvAsync_Init(&client->con);
433 BConnection_SendAsync_Init(&client->con);
434 StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->con);
435 StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->con);
436  
437 // init stream buffer (to loop received data back to the client)
438 if (!StreamBuffer_Init(&client->buf, BUF_SIZE, recv_if, send_if)) {
439 BLog(BLOG_ERROR, "StreamBuffer_Init failed");
440 goto fail2;
441 }
442  
443 // init disconnect timer
444 BTimer_Init(&client->disconnect_timer, options.disconnect_time, (BTimer_handler)client_disconnect_timer_handler, client);
445 BReactor_SetTimer(&ss, &client->disconnect_timer);
446  
447 // insert to clients list
448 LinkedList1_Append(&clients_list, &client->clients_list_node);
449 num_clients++;
450  
451 client_log(client, BLOG_INFO, "connected");
452 BLog(BLOG_NOTICE, "%d clients", num_clients);
453  
454 // update defense
455 update_defense();
456 return;
457  
458 fail2:
459 BConnection_SendAsync_Free(&client->con);
460 BConnection_RecvAsync_Free(&client->con);
461 BConnection_Free(&client->con);
462 fail1:
463 free(client);
464 fail0:
465 return;
466 }
467  
468 void client_free (struct client *client)
469 {
470 // remove from clients list
471 LinkedList1_Remove(&clients_list, &client->clients_list_node);
472 num_clients--;
473  
474 // free disconnect timer
475 BReactor_RemoveTimer(&ss, &client->disconnect_timer);
476  
477 // free stream buffer
478 StreamBuffer_Free(&client->buf);
479  
480 // free connection interfaces
481 BConnection_SendAsync_Free(&client->con);
482 BConnection_RecvAsync_Free(&client->con);
483  
484 // free connection
485 BConnection_Free(&client->con);
486  
487 // free structure
488 free(client);
489  
490 BLog(BLOG_NOTICE, "%d clients", num_clients);
491  
492 // update defense
493 update_defense();
494 }
495  
496 void client_logfunc (struct client *client)
497 {
498 char addr[BADDR_MAX_PRINT_LEN];
499 BAddr_Print(&client->addr, addr);
500  
501 BLog_Append("client (%s): ", addr);
502 }
503  
504 void client_log (struct client *client, int level, const char *fmt, ...)
505 {
506 va_list vl;
507 va_start(vl, fmt);
508 BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl);
509 va_end(vl);
510 }
511  
512 void client_disconnect_timer_handler (struct client *client)
513 {
514 client_log(client, BLOG_INFO, "timed out, disconnecting");
515  
516 // free client
517 client_free(client);
518 }
519  
520 void client_connection_handler (struct client *client, int event)
521 {
522 if (event == BCONNECTION_EVENT_RECVCLOSED) {
523 client_log(client, BLOG_INFO, "client closed");
524 } else {
525 client_log(client, BLOG_INFO, "client error");
526 }
527  
528 // free client
529 client_free(client);
530 }
531  
532 void update_defense (void)
533 {
534 #ifdef BADVPN_LINUX
535 if (options.defense_prepare_clients != -1) {
536 int val = num_clients >= options.defense_prepare_clients;
537 int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_PREPARE, &val, sizeof(val));
538 if (res < 0) {
539 BLog(BLOG_ERROR, "failed to %s defense preparation", (val ? "enable" : "disable"));
540 } else {
541 if (!defense_prepare && val) {
542 BLog(BLOG_NOTICE, "defense preparation enabled");
543 }
544 else if (defense_prepare && !val) {
545 BLog(BLOG_NOTICE, "defense preparation disabled");
546 }
547 }
548 defense_prepare = val;
549 }
550  
551 if (options.defense_activate_clients != -1) {
552 int val = num_clients >= options.defense_activate_clients;
553 int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_ACTIVATE, &val, sizeof(val));
554 if (res < 0) {
555 BLog(BLOG_ERROR, "failed to %s defense activation", (val ? "enable" : "disable"));
556 } else {
557 if (!defense_activate && val) {
558 BLog(BLOG_NOTICE, "defense activation enabled");
559 }
560 else if (defense_activate && !val) {
561 BLog(BLOG_NOTICE, "defense activation disabled");
562 }
563 }
564 defense_activate = val;
565 }
566 #endif
567 }