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 (at) 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 */ 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 */ 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 */ 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