1 /******************************************************************************/
2 /* */
3 /* Copyright (c) International Business Machines Corp., 2005 */
4 /* */
5 /* This program is free software; you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation; either version 2 of the License, or */
8 /* (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
13 /* the GNU General Public License for more details. */
14 /* */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program; if not, write to the Free Software */
17 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18 /* */
19 /******************************************************************************/
20
21 /*
22 * File:
23 * ns-tcpserver.c
24 *
25 * Description:
26 * This is TCP traffic server.
27 * Accept connections from the clients, then send tcp segments to clients
28 *
29 * Author:
30 * Mitsuru Chinen <mitch@jp.ibm.com>
31 *
32 * History:
33 * Oct 19 2005 - Created (Mitsuru Chinen)
34 *---------------------------------------------------------------------------*/
35
36 #include "ns-traffic.h"
37
38 /*
39 * Standard Include Files
40 */
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <netdb.h>
47 #include <time.h>
48 #include <unistd.h>
49 #include <sys/select.h>
50 #include <sys/socket.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
53 #include <sys/wait.h>
54 #include <netinet/in.h>
55 #include <netinet/tcp.h>
56
57 /*
58 * Gloval variables
59 */
60 struct sigaction handler; /* Behavior for a signal */
61 int catch_sighup; /* When catch the SIGHUP, set to non-zero */
62 int catch_sigpipe; /* When catch the SIGPIPE, set to non-zero */
63
64 /*
65 * Structure: server_info
66 *
67 * Description:
68 * This structure stores the information of a server
69 */
70 struct server_info {
71 sa_family_t family; /* protocol family */
72 char *portnum; /* port number */
73 int listen_sd; /* socket descriptor for listening */
74 int concurrent; /* if non-zero, act as a concurrent server */
75 size_t current_connection; /* number of the current connection */
76 size_t max_connection; /* maximum connection number */
77 size_t lost_connection; /* number of lost connection */
78 size_t small_sending; /* if non-zero, in the small sending mode */
79 size_t window_scaling; /* if non-zero, in the window scaling mode */
80 };
81
82 /*
83 * Function: usage()
84 *
85 * Descripton:
86 * Print the usage of this program. Then, terminate this program with
87 * the specified exit value.
88 *
89 * Argument:
90 * exit_value: exit value
91 *
92 * Return value:
93 * This function does not return.
94 */
usage(char * program_name,int exit_value)95 void usage(char *program_name, int exit_value)
96 {
97 FILE *stream = stdout; /* stream where the usage is output */
98
99 if (exit_value == EXIT_FAILURE)
100 stream = stderr;
101
102 fprintf(stream, "%s [OPTION]\n"
103 "\t-f\tprotocol family\n"
104 "\t\t 4 : IPv4\n"
105 "\t\t 6 : IPv6\n"
106 "\t-p\tport number\n"
107 "\t-b\twork in the background\n"
108 "\t-c\twork in the concurrent server mode\n"
109 "\t-s\twork in the small sending mode\n"
110 "\t-w\twork in the window scaling mode\n"
111 "\t-o\tfilename where the server infomation is outputted\n"
112 "\t-d\twork in the debug mode\n"
113 "\t-h\tdisplay this usage\n"
114 "" "*) Server works till it receives SIGHUP\n", program_name);
115 exit(exit_value);
116 }
117
118 /*
119 * Function: set_signal_flag()
120 *
121 * Description:
122 * This function sets global variable according to the signal.
123 * Once a signal is caught, the signal is ignored after that.
124 *
125 * Argument:
126 * type: type of signal
127 *
128 * Return value:
129 * None
130 */
set_signal_flag(int type)131 void set_signal_flag(int type)
132 {
133 /* Set SIG_IGN against the caught signal */
134 handler.sa_handler = SIG_IGN;
135 if (sigaction(type, &handler, NULL) < 0)
136 fatal_error("sigaction()");
137
138 if (debug)
139 fprintf(stderr, "Catch signal. type is %d\n", type);
140
141 switch (type) {
142 case SIGHUP:
143 catch_sighup = 1;
144 break;
145 case SIGPIPE:
146 catch_sigpipe = 1;
147 break;
148 default:
149 fprintf(stderr, "Unexpected signal (%d) is caught\n", type);
150 exit(EXIT_FAILURE);
151 }
152 }
153
154 /*
155 * Function: delete_zombies()
156 *
157 * Descripton:
158 * Delete the zombies
159 *
160 * Argument:
161 * info_p: pointer to a server infomation
162 *
163 * Return value:
164 * None
165 */
delete_zombies(struct server_info * info_p)166 void delete_zombies(struct server_info *info_p)
167 {
168 int status; /* exit value of a child */
169 pid_t zombie_pid; /* process id of a zombie */
170
171 while (info_p->current_connection) {
172 zombie_pid = waitpid((pid_t) - 1, &status, WNOHANG);
173 if (zombie_pid == (pid_t) - 1)
174 fatal_error("waitpid()");
175 else if (zombie_pid == (pid_t) 0)
176 break;
177 else {
178 --info_p->current_connection;
179 if (status != EXIT_SUCCESS) {
180 ++info_p->lost_connection;
181 if (debug)
182 fprintf(stderr,
183 "The number of lost conncections is %zu\n",
184 info_p->lost_connection);
185 }
186 }
187 }
188 }
189
190 /*
191 * Function: create_listen_socket()
192 *
193 * Descripton:
194 * Create a socket to listen for connections on a socket.
195 * The socket discripter is stored info_p->listen_sd.
196 *
197 * Argument:
198 * info_p: pointer to a server infomation
199 *
200 * Return value:
201 * None
202 */
create_listen_socket(struct server_info * info_p)203 void create_listen_socket(struct server_info *info_p)
204 {
205 int on; /* on/off at an socket option */
206 int err; /* return value of getaddrinfo */
207 struct addrinfo hints; /* hints for getaddrinfo() */
208 struct addrinfo *res; /* pointer to addrinfo */
209
210 /* Set the hints to addrinfo() */
211 memset(&hints, '\0', sizeof(struct addrinfo));
212 hints.ai_family = info_p->family;
213 hints.ai_socktype = SOCK_STREAM;
214 hints.ai_protocol = IPPROTO_TCP;
215 hints.ai_flags = AI_PASSIVE;
216
217 /* Translate the network and service information of the server */
218 err = getaddrinfo(NULL, info_p->portnum, &hints, &res);
219 if (err) {
220 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err));
221 exit(EXIT_FAILURE);
222 }
223 if (res->ai_next) {
224 fprintf(stderr, "getaddrinfo(): multiple address is found.");
225 exit(EXIT_FAILURE);
226 }
227
228 /* Create a socket for listening. */
229 info_p->listen_sd = socket(res->ai_family,
230 res->ai_socktype, res->ai_protocol);
231 if (info_p->listen_sd < 0)
232 fatal_error("socket()");
233
234 #ifdef IPV6_V6ONLY
235 /* Don't accept IPv4 mapped address if the protocol family is IPv6 */
236 if (res->ai_family == PF_INET6) {
237 on = 1;
238 if (setsockopt(info_p->listen_sd,
239 IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(int)))
240 fatal_error("setsockopt()");
241 }
242 #endif
243
244 /* Enable to reuse the socket */
245 on = 1;
246 if (setsockopt(info_p->listen_sd,
247 SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)))
248 fatal_error("setsockopt()");
249
250 /* Disable the Nagle algorithm, when small sending mode */
251 if (info_p->small_sending) {
252 on = 1;
253 if (setsockopt(info_p->listen_sd,
254 IPPROTO_TCP, TCP_NODELAY, &on, sizeof(int)))
255 fatal_error("setsockopt()");
256 if (debug) {
257 fprintf(stderr, "small sending[on]\n");
258 }
259 }
260
261 /* Maximize socket buffer, when window scaling mode */
262 if (info_p->window_scaling)
263 maximize_sockbuf(info_p->listen_sd);
264
265 /* Bind to the local address */
266 if (bind(info_p->listen_sd, res->ai_addr, res->ai_addrlen) < 0)
267 fatal_error("bind()");
268 freeaddrinfo(res);
269
270 /* Start to listen for connections */
271 if (listen(info_p->listen_sd, 5) < 0)
272 fatal_error("listen()");
273 }
274
275 /*
276 * Function: communicate_client()
277 *
278 * Descripton:
279 * Communicate with the connectted client.
280 * Currently, this function sends tcp segment in the specified second
281 * or recevie SIGHUP
282 *
283 * Argument:
284 * sock_fd: socket descriptor to communicate with client
285 * info_p: pointer to a server infomation
286 *
287 * Return value:
288 * 0: success
289 * other: fail
290 */
communicate_client(struct server_info * info_p,int sock_fd)291 int communicate_client(struct server_info *info_p, int sock_fd)
292 {
293 char *sendmsg; /* pointer to the message to send */
294 int sndbuf_size; /* size of the send buffer */
295 socklen_t sock_optlen; /* size of the result parameter */
296 ssize_t sntbyte_size; /* size of the sent byte */
297 int ret = EXIT_SUCCESS; /* The return value of this function */
298
299 if (info_p->small_sending) { /* small sending mode */
300 sndbuf_size = 1;
301 } else {
302 sock_optlen = sizeof(sndbuf_size);
303 if (getsockopt
304 (sock_fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size,
305 &sock_optlen) < 0) {
306 perror("getsockopt()");
307 if (close(sock_fd))
308 fatal_error("close()");
309 return EXIT_FAILURE;
310 }
311 }
312 if (debug)
313 fprintf(stderr, "sndbuf size is %d\n", sndbuf_size);
314
315 /* Define the message */
316 sendmsg = malloc(sndbuf_size);
317 if (sendmsg == NULL) {
318 fprintf(stderr, "malloc() is failed.\n");
319 if (close(sock_fd))
320 fatal_error("close()");
321 return EXIT_FAILURE;
322 }
323
324 /* Set a signal handler against SIGHUP and SIGPIPE */
325 handler.sa_handler = set_signal_flag;
326 if (sigaction(SIGHUP, &handler, NULL) < 0)
327 fatal_error("sigaction()");
328 if (sigaction(SIGPIPE, &handler, NULL) < 0)
329 fatal_error("sigaction()");
330
331 /* Send the message */
332 for (;;) {
333 sntbyte_size = send(sock_fd, sendmsg, sndbuf_size, 0);
334
335 /* Catch SIGPIPE */
336 if (catch_sigpipe) {
337 if (debug)
338 fprintf(stderr,
339 "The client closed the connection.\n");
340 break;
341 }
342
343 /* Catch SIGHUP */
344 if (catch_sighup)
345 break;
346
347 if (sntbyte_size < (ssize_t) 0) {
348 if (errno == EPIPE) {
349 if (debug)
350 fprintf(stderr,
351 "The client closed the connection.\n");
352 } else {
353 printf("errno=%d\n", errno);
354 perror("send()");
355 ret = EXIT_FAILURE;
356 }
357 break;
358 }
359 }
360
361 free(sendmsg);
362 if (close(sock_fd))
363 fatal_error("close()");
364 return ret;
365 }
366
367 /*
368 * Function: handle_client()
369 *
370 * Descripton:
371 * Accept a connection from a client, then fork to communicate the client
372 *
373 * Argument:
374 * info_p: pointer to a server infomation
375 *
376 * Return value:
377 * 0: success
378 * other: fail
379 */
handle_client(struct server_info * info_p)380 int handle_client(struct server_info *info_p)
381 {
382 int ret = EXIT_SUCCESS; /* return value of this function */
383 int do_accept = 1; /* if non-zero, accept connection */
384 fd_set read_fds; /* list of file descriptor for reading */
385 int max_read_fd = 0; /* maximum number in the read fds */
386
387 info_p->current_connection = 0;
388 FD_ZERO(&read_fds);
389 FD_SET(info_p->listen_sd, &read_fds);
390 max_read_fd = info_p->listen_sd;
391
392 /* Catch SIGHUP */
393 handler.sa_handler = set_signal_flag;
394 if (sigaction(SIGHUP, &handler, NULL) < 0)
395 fatal_error("sigaction()");
396
397 /* Loop to wait a new connection */
398 for (;;) {
399 if (do_accept) {
400 int data_sd; /* socket descriptor for send/recv data */
401 socklen_t client_addr_len; /* length of `client_addr' */
402 struct sockaddr_storage client_addr; /* address of a client */
403 int select_ret; /* return value of select() */
404 fd_set active_fds; /* list of the active file descriptor */
405 struct timeval select_timeout; /* timeout for select() */
406
407 /* When catch SIGHUP, no more connection is acceptted. */
408 if (catch_sighup) {
409 do_accept = 0;
410 if (close(info_p->listen_sd))
411 fatal_error("close()");
412 continue;
413 }
414
415 /* Check a connection is requested */
416 active_fds = read_fds;
417 select_timeout.tv_sec = 0; /* 0.5 sec */
418 select_timeout.tv_usec = 500000;
419
420 select_ret = select(max_read_fd + 1,
421 &active_fds, NULL, NULL,
422 &select_timeout);
423 if (select_ret < 0) {
424 do_accept = 0;
425 if (!catch_sighup) {
426 perror("select()");
427 ret = EXIT_FAILURE;
428 }
429 if (close(info_p->listen_sd))
430 fatal_error("close()");
431 continue;
432 } else if (select_ret == 0) { /* select() is timeout */
433 if (info_p->concurrent)
434 delete_zombies(info_p);
435 continue;
436 }
437
438 /* Accetpt a client connection */
439 if (FD_ISSET(info_p->listen_sd, &active_fds)) {
440 client_addr_len =
441 sizeof(struct sockaddr_storage);
442 data_sd =
443 accept(info_p->listen_sd,
444 (struct sockaddr *)&client_addr,
445 &client_addr_len);
446 if (data_sd < 0) {
447 do_accept = 0;
448 if (!catch_sighup) {
449 perror("accept()");
450 ret = EXIT_FAILURE;
451 }
452 if (close(info_p->listen_sd))
453 fatal_error("close()");
454 continue;
455 }
456 if (debug)
457 fprintf(stderr,
458 "called accept(). data_sd=%d\n",
459 data_sd);
460
461 /* Handle clients */
462 if (info_p->concurrent) { /* concurrent server. */
463 pid_t child_pid;
464 child_pid = fork();
465 if (child_pid < 0) { /* fork() is failed. */
466 perror("fork()");
467 if (close(data_sd))
468 fatal_error("close()");
469 if (close(info_p->listen_sd))
470 fatal_error("close()");
471 do_accept = 0;
472 continue;
473 } else if (child_pid == 0) { /* case of a child */
474 int exit_value;
475 if (close(info_p->listen_sd))
476 fatal_error("close()");
477 exit_value =
478 communicate_client(info_p,
479 data_sd);
480 if (debug)
481 fprintf(stderr,
482 "child(%d) exits. value is %d\n",
483 getpid(),
484 exit_value);
485 exit(exit_value);
486 } else { /* case of the parent */
487 if (close(data_sd))
488 fatal_error("close()");
489
490 ++info_p->current_connection;
491 if (info_p->max_connection <
492 info_p->
493 current_connection) {
494 info_p->max_connection =
495 info_p->
496 current_connection;
497 if (debug)
498 fprintf(stderr,
499 "The maximum connection is updated. The number is %zu.\n",
500 info_p->
501 max_connection);
502 }
503 delete_zombies(info_p);
504 }
505 } else { /* repeat server */
506 ret =
507 communicate_client(info_p, data_sd);
508 if (ret != EXIT_SUCCESS)
509 if (close(info_p->listen_sd))
510 fatal_error("close()");
511 break;
512 }
513 }
514 } else {
515 /* case where new connection isn't accepted. */
516 if (info_p->concurrent)
517 delete_zombies(info_p);
518 if (info_p->current_connection == 0)
519 break;
520 }
521 }
522 return ret;
523 }
524
525 /*
526 *
527 * Function: main()
528 *
529 */
main(int argc,char * argv[])530 int main(int argc, char *argv[])
531 {
532 char *program_name = argv[0];
533 int optc; /* option */
534 struct server_info server; /* server information */
535 int ret = EXIT_SUCCESS; /* exit value */
536 int background = 0; /* If non-zero work in the background */
537 FILE *info_fp = stdout; /* FILE pointer to a information file */
538
539 debug = 0;
540
541 /* Initilalize the server information */
542 memset(&server, '\0', sizeof(struct server_info));
543 server.family = PF_UNSPEC;
544 server.portnum = NULL;
545
546 /* Retrieve the options */
547 while ((optc = getopt(argc, argv, "f:p:bcswo:dh")) != EOF) {
548 switch (optc) {
549 case 'f':
550 if (strncmp(optarg, "4", 1) == 0)
551 server.family = PF_INET; /* IPv4 */
552 else if (strncmp(optarg, "6", 1) == 0)
553 server.family = PF_INET6; /* IPv6 */
554 else {
555 fprintf(stderr,
556 "protocol family should be 4 or 6.\n");
557 usage(program_name, EXIT_FAILURE);
558 }
559 break;
560
561 case 'p':
562 {
563 unsigned long int num;
564 num = strtoul(optarg, NULL, 0);
565 if (num < PORTNUMMIN || PORTNUMMAX < num) {
566 fprintf(stderr,
567 "The range of port is from %u to %u\n",
568 PORTNUMMIN, PORTNUMMAX);
569 usage(program_name, EXIT_FAILURE);
570 }
571 server.portnum = strdup(optarg);
572 }
573 break;
574
575 case 'b':
576 background = 1;
577 break;
578
579 case 'c':
580 server.concurrent = 1;
581 break;
582
583 case 's':
584 server.small_sending = 1;
585 break;
586
587 case 'w':
588 server.window_scaling = 1;
589 break;
590
591 case 'o':
592 if ((info_fp = fopen(optarg, "w")) == NULL) {
593 fprintf(stderr, "Cannot open %s\n", optarg);
594 exit(EXIT_FAILURE);
595 }
596 break;
597
598 case 'd':
599 debug = 1;
600 break;
601
602 case 'h':
603 usage(program_name, EXIT_SUCCESS);
604 break;
605
606 default:
607 usage(program_name, EXIT_FAILURE);
608 }
609 }
610
611 /* Check the family is spefied. */
612 if (server.family == PF_UNSPEC) {
613 fprintf(stderr, "protocol family should be specified.\n");
614 usage(program_name, EXIT_FAILURE);
615 }
616
617 /* Check the port number is specfied. */
618 if (server.portnum == NULL) {
619 server.portnum = (char *)calloc(6, sizeof(char));
620 sprintf(server.portnum, "%u", PORTNUMMIN);
621 }
622
623 /* If -b option is specified, work as a daemon */
624 if (background)
625 if (daemon(0, 0) < 0)
626 fatal_error("daemon()");
627
628 /* At first, SIGHUP is ignored. default with SIGPIPE */
629 handler.sa_handler = SIG_IGN;
630 if (sigfillset(&handler.sa_mask) < 0)
631 fatal_error("sigfillset()");
632 handler.sa_flags = 0;
633
634 if (sigaction(SIGHUP, &handler, NULL) < 0)
635 fatal_error("sigaction()");
636
637 /* Create a listen socket */
638 create_listen_socket(&server);
639
640 /* Output any server information to the information file */
641 fprintf(info_fp, "PID: %u\n", getpid());
642 fflush(info_fp);
643 if (info_fp != stdout)
644 if (fclose(info_fp))
645 fatal_error("fclose()");
646
647 /* Handle one or more tcp clients. */
648 ret = handle_client(&server);
649 exit(ret);
650 }
651