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 */
41struct sigaction handler;	/* Behavior for a signal */
42int 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 */
74void 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 */
107void 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 */
131int 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