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