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-tcpclient.c 24 * 25 * Description: 26 * This is TCP traffic client. 27 * Request connections to the server, then receive tcp segments 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 * Gloval variables 40 */ 41 struct sigaction handler; /* Behavior for a signal */ 42 int catch_sighup; /* When catch the SIGHUP, set to non-zero */ 43 44 /* 45 * Standard Header Files 46 */ 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <netdb.h> 53 #include <time.h> 54 #include <unistd.h> 55 #include <sys/socket.h> 56 #include <sys/stat.h> 57 #include <sys/types.h> 58 #include <sys/wait.h> 59 #include <netinet/in.h> 60 61 /* 62 * Function: usage() 63 * 64 * Descripton: 65 * Print the usage of this program. Then, terminate this program with 66 * the specified exit value. 67 * 68 * Argument: 69 * exit_value: exit value 70 * 71 * Return value: 72 * This function does not return. 73 */ 74 void usage(char *program_name, int exit_value) 75 { 76 FILE *stream = stdout; /* stream where the usage is output */ 77 78 if (exit_value == EXIT_FAILURE) 79 stream = stderr; 80 81 fprintf(stream, "%s [OPTION]\n" 82 "\t-S\tname or IP address of the server\n" 83 "\t-f\tprotocol family\n" 84 "\t\t 4 : IPv4\n" 85 "\t\t 6 : IPv6\n" 86 "\t-p\tport number\n" 87 "\t-t\ttimeout [sec]\n" 88 "\t-b\twork in the background\n" 89 "\t-w\twork in the window scaling mode\n" 90 "\t-d\tdisplay debug informations\n" 91 "\t-h\tdisplay this usage\n", program_name); 92 exit(exit_value); 93 } 94 95 /* 96 * Function: set_signal_flag() 97 * 98 * Description: 99 * This function sets global variables accordig to signal 100 * 101 * Argument: 102 * type: type of signal 103 * 104 * Return value: 105 * None 106 */ 107 void set_signal_flag(int type) 108 { 109 if (debug) 110 fprintf(stderr, "Catch signal. type is %d\n", type); 111 112 switch (type) { 113 case SIGHUP: 114 catch_sighup = 1; 115 handler.sa_handler = SIG_IGN; 116 if (sigaction(type, &handler, NULL) < 0) 117 fatal_error("sigaction()"); 118 break; 119 120 default: 121 fprintf(stderr, "Unexpected signal (%d) is caught\n", type); 122 exit(EXIT_FAILURE); 123 } 124 } 125 126 /* 127 * 128 * Function: main() 129 * 130 */ 131 int main(int argc, char *argv[]) 132 { 133 char *program_name = argv[0]; 134 int optc; /* option */ 135 int sock_fd; /* socket descriptor for a connection */ 136 char *server_name; /* Name (or IP address) of the server */ 137 sa_family_t family; /* protocol family */ 138 char *portnum; /* port number in string representation */ 139 struct addrinfo hints; /* hints for getaddrinfo() */ 140 struct addrinfo *res; /* pointer to addrinfo structure */ 141 int err; /* return value of getaddrinfo */ 142 int on; /* on/off at an socket option */ 143 int recvbuf_size; /* size of the receive buffer */ 144 socklen_t sock_optlen; /* size of the result parameter */ 145 char *recvbuf; /* pointer to the received message */ 146 ssize_t recvbyte_size; /* size of the receive byte */ 147 time_t start_time; /* time when the timer is start */ 148 double timeout = 0.0; /* timeout */ 149 int background = 0; /* If non-zero work in the background */ 150 size_t window_scaling = 0; /* if non-zero, in the window scaling mode */ 151 152 debug = 0; 153 154 /* Initilalize the client information */ 155 family = PF_UNSPEC; 156 server_name = NULL; 157 portnum = NULL; 158 159 /* Retrieve the options */ 160 while ((optc = getopt(argc, argv, "S:f:p:t:bwdh")) != EOF) { 161 switch (optc) { 162 case 'S': 163 server_name = strdup(optarg); 164 if (server_name == NULL) { 165 fprintf(stderr, "strdup() failed."); 166 exit(EXIT_FAILURE); 167 } 168 break; 169 170 case 'f': 171 if (strncmp(optarg, "4", 1) == 0) 172 family = PF_INET; /* IPv4 */ 173 else if (strncmp(optarg, "6", 1) == 0) 174 family = PF_INET6; /* IPv6 */ 175 else { 176 fprintf(stderr, 177 "protocol family should be 4 or 6.\n"); 178 usage(program_name, EXIT_FAILURE); 179 } 180 break; 181 182 case 'p': 183 { 184 unsigned long int tmp; 185 tmp = strtoul(optarg, NULL, 0); 186 if (tmp < PORTNUMMIN || PORTNUMMAX < tmp) { 187 fprintf(stderr, 188 "The range of port is from %u to %u\n", 189 PORTNUMMIN, PORTNUMMAX); 190 usage(program_name, EXIT_FAILURE); 191 } 192 portnum = strdup(optarg); 193 } 194 break; 195 196 case 't': 197 timeout = strtod(optarg, NULL); 198 if (timeout < 0) { 199 fprintf(stderr, 200 "Timeout value is bigger than 0\n"); 201 usage(program_name, EXIT_FAILURE); 202 } 203 break; 204 205 case 'b': 206 background = 1; 207 break; 208 209 case 'w': 210 window_scaling = 1; 211 break; 212 213 case 'd': 214 debug = 1; 215 break; 216 217 case 'h': 218 usage(program_name, EXIT_SUCCESS); 219 break; 220 221 default: 222 usage(program_name, EXIT_FAILURE); 223 } 224 } 225 226 /* Check the server name is specified. */ 227 if (server_name == NULL) { 228 fprintf(stderr, "server name isn't specified.\n"); 229 usage(program_name, EXIT_FAILURE); 230 } 231 232 /* Check the family is specified. */ 233 if (family == PF_UNSPEC) { 234 fprintf(stderr, "protocol family isn't specified.\n"); 235 usage(program_name, EXIT_FAILURE); 236 } 237 238 /* Check the port number is specified. */ 239 if (portnum == NULL) { 240 fprintf(stderr, "port number isn't specified.\n"); 241 usage(program_name, EXIT_FAILURE); 242 } 243 244 /* At first, SIGHUP are Ignored. */ 245 handler.sa_handler = set_signal_flag; 246 handler.sa_flags = 0; 247 if (sigfillset(&handler.sa_mask) < 0) 248 fatal_error("sigfillset()"); 249 if (sigaction(SIGHUP, &handler, NULL) < 0) 250 fatal_error("sigaction()"); 251 252 /* Set the hints to addrinfo() */ 253 memset(&hints, '\0', sizeof(struct addrinfo)); 254 hints.ai_family = family; 255 hints.ai_socktype = SOCK_STREAM; 256 hints.ai_protocol = IPPROTO_TCP; 257 258 /* Translate the network and service information of the client */ 259 err = getaddrinfo(server_name, portnum, &hints, &res); 260 if (err) { 261 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err)); 262 exit(EXIT_FAILURE); 263 } 264 if (res->ai_next) { 265 fprintf(stderr, "getaddrinfo(): multiple address is found."); 266 exit(EXIT_FAILURE); 267 } 268 269 /* Create a socket */ 270 sock_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 271 if (sock_fd < 0) 272 fatal_error("socket()"); 273 274 /* Enable to reuse the socket */ 275 on = 1; 276 if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int))) 277 fatal_error("setsockopt()"); 278 279 /* Maximize socket buffer, when window scaling mode */ 280 if (window_scaling) 281 maximize_sockbuf(sock_fd); 282 283 /* Connect to the server */ 284 if (connect(sock_fd, res->ai_addr, res->ai_addrlen) < 0) 285 fatal_error("connect()"); 286 287 freeaddrinfo(res); 288 free(server_name); 289 290 /* If -b option is specified, work as a daemon */ 291 if (background) 292 if (daemon(0, 0) < 0) 293 fatal_error("daemon()"); 294 295 /* Get the size of receive buffer */ 296 sock_optlen = sizeof(recvbuf_size); 297 if (getsockopt 298 (sock_fd, SOL_SOCKET, SO_RCVBUF, &recvbuf_size, &sock_optlen) < 0) 299 fatal_error("getsockopt()"); 300 if (debug) 301 fprintf(stderr, "recvbuf size of socket(%d) is %d\n", sock_fd, 302 recvbuf_size); 303 304 /* Prepare a buffer to receive bytes */ 305 recvbuf = malloc(recvbuf_size); 306 if (recvbuf == NULL) { 307 fprintf(stderr, "malloc() is failed.\n"); 308 exit(EXIT_FAILURE); 309 } 310 311 /* 312 * Loop for receiving data from the server 313 */ 314 start_time = time(NULL); 315 handler.sa_handler = set_signal_flag; 316 if (sigaction(SIGHUP, &handler, NULL) < 0) 317 fatal_error("sigaction()"); 318 for (;;) { 319 recvbyte_size = recv(sock_fd, recvbuf, recvbuf_size, 0); 320 if (recvbyte_size < (ssize_t) 0) { 321 if (catch_sighup) 322 break; 323 else 324 fatal_error("sendto()"); 325 } else if (recvbyte_size == (ssize_t) 0) 326 break; 327 328 /* client timeout */ 329 if (timeout) 330 if (timeout < difftime(time(NULL), start_time)) 331 break; 332 333 /* Catch SIGHUP */ 334 if (catch_sighup) 335 break; 336 } 337 if (close(sock_fd) < 0) 338 fatal_error("close()"); 339 340 free(recvbuf); 341 342 if (debug) 343 fprintf(stderr, "Client is finished without any error\n"); 344 345 exit(EXIT_SUCCESS); 346 } 347