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