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-udpserver.c 24 * 25 * Description: 26 * This is UDP traffic server. 27 * Received UDP datagram from a client, then send it to the client 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 * Standard Include Files 40 */ 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <netdb.h> 47 #include <time.h> 48 #include <unistd.h> 49 #include <sys/select.h> 50 #include <sys/socket.h> 51 #include <sys/stat.h> 52 #include <sys/types.h> 53 #include <sys/wait.h> 54 #include <netinet/in.h> 55 56 /* 57 * Gloval variables 58 */ 59 struct sigaction handler; /* Behavior for a signal */ 60 int catch_sighup; /* When catch the SIGHUP, set to non-zero */ 61 62 /* 63 * Function: usage() 64 * 65 * Descripton: 66 * Print the usage of this program. Then, terminate this program with 67 * the specified exit value. 68 * 69 * Argument: 70 * exit_value: exit value 71 * 72 * Return value: 73 * This function does not return. 74 */ 75 void usage(char *program_name, int exit_value) 76 { 77 FILE *stream = stdout; /* stream where the usage is output */ 78 79 if (exit_value == EXIT_FAILURE) 80 stream = stderr; 81 82 fprintf(stream, "%s [OPTION]\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\tthe port number specified by -p option would be the first port number\n" 88 "\t-b\twork in the background\n" 89 "\t-o\tfilename where the server infomation is outputted\n" 90 "\t-d\twork in the debug mode\n" 91 "\t-h\tdisplay this usage\n" 92 "" "*) Server works till it receives SIGHUP\n", program_name); 93 exit(exit_value); 94 } 95 96 /* 97 * Function: set_signal_flag() 98 * 99 * Description: 100 * This function sets global variable according to the signal. 101 * Once a signal is caught, the signal is ignored after that. 102 * 103 * Argument: 104 * type: type of signal 105 * 106 * Return value: 107 * None 108 */ 109 void set_signal_flag(int type) 110 { 111 /* Set SIG_IGN against the caught signal */ 112 handler.sa_handler = SIG_IGN; 113 if (sigaction(type, &handler, NULL) < 0) 114 fatal_error("sigaction()"); 115 116 if (debug) 117 fprintf(stderr, "Catch signal. type is %d\n", type); 118 119 switch (type) { 120 case SIGHUP: 121 catch_sighup = 1; 122 break; 123 default: 124 fprintf(stderr, "Unexpected signal (%d) is caught\n", type); 125 exit(EXIT_FAILURE); 126 } 127 } 128 129 /* 130 * Function: respond_to_client() 131 * 132 * Description: 133 * Recieve the client data. Then, return the data to client. 134 * 135 * Argument: 136 * sock_fd: socket file descriptor 137 * 138 * Return value: 139 * None 140 */ 141 void respond_to_client(sock_fd) 142 { 143 char *msgbuf; /* Pointer to the message */ 144 size_t msgbuf_size; /* size of msgbuf */ 145 ssize_t msglen; /* the length of message */ 146 socklen_t sock_optlen; /* size of the result parameter */ 147 struct sockaddr_storage client_addr; /* address of a client */ 148 socklen_t client_addr_len; /* length of `client_addr' */ 149 150 sock_optlen = sizeof(sock_optlen); 151 if (getsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, 152 &msgbuf_size, &sock_optlen) < 0) { 153 perror("getsockopt()"); 154 close(sock_fd); 155 exit(EXIT_FAILURE); 156 } 157 158 /* Allocate the memory for a message */ 159 msgbuf = malloc(msgbuf_size + 1); 160 if (msgbuf == NULL) { 161 fprintf(stderr, "malloc() is failed.\n"); 162 close(sock_fd); 163 exit(EXIT_FAILURE); 164 } 165 166 /* Receive a message */ 167 client_addr_len = sizeof(client_addr); 168 if ((msglen = recvfrom(sock_fd, msgbuf, msgbuf_size, 0, 169 (struct sockaddr *)&client_addr, 170 &client_addr_len)) < 0) 171 fatal_error("recvfrom()"); 172 msgbuf[msglen] = '\0'; 173 174 if (debug) 175 fprintf(stderr, "Message is %s\n", msgbuf); 176 177 /* Return the message to the client */ 178 if (sendto(sock_fd, msgbuf, msglen, 0, 179 (struct sockaddr *)&client_addr, 180 sizeof(client_addr)) != msglen) 181 fatal_error("sendto()"); 182 free(msgbuf); 183 } 184 185 /* 186 * 187 * Function: main() 188 * 189 */ 190 int main(int argc, char *argv[]) 191 { 192 char *program_name = argv[0]; 193 int optc; /* option */ 194 sa_family_t family; /* protocol family */ 195 char *portnum = NULL; /* port number */ 196 int sock_fd; /* socket binded open ports */ 197 int background = 0; /* work in the background if non-zero */ 198 fd_set read_fds; /* list of file descriptor for reading */ 199 int max_read_fd = 0; /* maximum number in the read fds */ 200 FILE *info_fp = stdout; /* FILE pointer to a information file */ 201 int on; /* on/off at an socket option */ 202 int err; /* return value of getaddrinfo */ 203 struct addrinfo hints; /* hints for getaddrinfo() */ 204 struct addrinfo *res; /* pointer to addrinfo */ 205 206 debug = 0; 207 family = PF_UNSPEC; 208 209 /* Retrieve the options */ 210 while ((optc = getopt(argc, argv, "f:p:bo:dh")) != EOF) { 211 switch (optc) { 212 case 'f': 213 if (strncmp(optarg, "4", 1) == 0) 214 family = PF_INET; /* IPv4 */ 215 else if (strncmp(optarg, "6", 1) == 0) 216 family = PF_INET6; /* IPv6 */ 217 else { 218 fprintf(stderr, 219 "protocol family should be 4 or 6.\n"); 220 usage(program_name, EXIT_FAILURE); 221 } 222 break; 223 224 case 'p': 225 { 226 unsigned long int num; 227 num = strtoul(optarg, NULL, 0); 228 if (num < PORTNUMMIN || PORTNUMMAX < num) { 229 fprintf(stderr, 230 "The range of port is from %u to %u\n", 231 PORTNUMMIN, PORTNUMMAX); 232 usage(program_name, EXIT_FAILURE); 233 } 234 portnum = strdup(optarg); 235 } 236 break; 237 238 case 'b': 239 background = 1; 240 break; 241 242 case 'o': 243 if ((info_fp = fopen(optarg, "w")) == NULL) { 244 fprintf(stderr, "Cannot open %s\n", optarg); 245 exit(EXIT_FAILURE); 246 } 247 break; 248 249 case 'd': 250 debug = 1; 251 break; 252 253 case 'h': 254 usage(program_name, EXIT_SUCCESS); 255 break; 256 257 default: 258 usage(program_name, EXIT_FAILURE); 259 } 260 } 261 262 /* Check the family is spefied. */ 263 if (family == PF_UNSPEC) { 264 fprintf(stderr, "protocol family should be specified.\n"); 265 usage(program_name, EXIT_FAILURE); 266 } 267 268 /* At first, SIGHUP is ignored. */ 269 handler.sa_handler = SIG_IGN; 270 if (sigfillset(&handler.sa_mask) < 0) 271 fatal_error("sigfillset()"); 272 handler.sa_flags = 0; 273 274 if (sigaction(SIGHUP, &handler, NULL) < 0) 275 fatal_error("sigaction()"); 276 277 /* Set the hints to addrinfo() */ 278 memset(&hints, '\0', sizeof(struct addrinfo)); 279 hints.ai_family = family; 280 hints.ai_socktype = SOCK_DGRAM; 281 hints.ai_protocol = IPPROTO_UDP; 282 hints.ai_flags = AI_PASSIVE; 283 284 /* Translate the network and service information of the server */ 285 err = getaddrinfo(NULL, portnum, &hints, &res); 286 if (err) { 287 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err)); 288 exit(EXIT_FAILURE); 289 } 290 if (res->ai_next) { 291 fprintf(stderr, "getaddrinfo(): multiple address is found."); 292 exit(EXIT_FAILURE); 293 } 294 295 /* Create a socket for listening. */ 296 sock_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 297 if (sock_fd < 0) 298 fatal_error("socket()"); 299 300 #ifdef IPV6_V6ONLY 301 /* Don't accept IPv4 mapped address if the protocol family is IPv6 */ 302 if (res->ai_family == PF_INET6) { 303 on = 1; 304 if (setsockopt 305 (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(int))) 306 fatal_error("setsockopt()"); 307 } 308 #endif 309 310 /* Enable to reuse the socket */ 311 on = 1; 312 if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int))) 313 fatal_error("setsockopt()"); 314 315 /* Bind to the local address */ 316 if (bind(sock_fd, res->ai_addr, res->ai_addrlen) < 0) 317 fatal_error("bind()"); 318 319 freeaddrinfo(res); 320 321 /* If -b option is specified, work as a daemon */ 322 if (background) 323 if (daemon(0, 0) < 0) 324 fatal_error("daemon()"); 325 326 /* Output any server information to the information file */ 327 fprintf(info_fp, "PID: %u\n", getpid()); 328 fflush(info_fp); 329 if (info_fp != stdout) 330 if (fclose(info_fp)) 331 fatal_error("fclose()"); 332 333 /* Catch SIGHUP */ 334 handler.sa_handler = set_signal_flag; 335 if (sigaction(SIGHUP, &handler, NULL) < 0) 336 fatal_error("sigaction()"); 337 338 /* Loop to wait a client access */ 339 FD_ZERO(&read_fds); 340 FD_SET(sock_fd, &read_fds); 341 max_read_fd = sock_fd; 342 for (;;) { 343 int select_ret; /* return value of select() */ 344 fd_set active_fds; /* list of the active file descriptor */ 345 struct timeval select_timeout; /* timeout for select() */ 346 347 /* When catch SIGHUP, exit the loop */ 348 if (catch_sighup) 349 break; 350 351 active_fds = read_fds; 352 select_timeout.tv_sec = 0; /* 0.5 sec */ 353 select_timeout.tv_usec = 500000; 354 355 select_ret = select(max_read_fd + 1, &active_fds, 356 NULL, NULL, &select_timeout); 357 if (select_ret < 0) { 358 if (catch_sighup) 359 break; 360 else 361 fatal_error("select()"); 362 } else if (select_ret == 0) { 363 continue; 364 } else { 365 if (FD_ISSET(sock_fd, &active_fds)) 366 respond_to_client(sock_fd); 367 } 368 } 369 370 /* Close the sockets */ 371 if (close(sock_fd)) 372 fatal_error("close()"); 373 374 exit(EXIT_SUCCESS); 375 } 376