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@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  */
usage(char * program_name,int exit_value)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  */
set_signal_flag(int type)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  */
main(int argc,char * argv[])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