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