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 (at) 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 */ 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 */ 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 */ 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 */ 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 connected 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 */ 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 */ 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 */ 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