diff options
| author | David M. Syzdek <david@syzdek.net> | 2022-01-13 17:04:01 -0900 |
|---|---|---|
| committer | David M. Syzdek <david@syzdek.net> | 2022-01-13 20:48:03 -0900 |
| commit | 8f60a9558933e80d3f93fbd3af55230f5b32108e (patch) | |
| tree | 736b3bc8f067287d2616a62f7873d888be00de50 /examples/socket-example.c | |
| parent | e3c2a2f50e8ee834296aec7fd6cdf96475907591 (diff) | |
| download | tinyrad-8f60a9558933e80d3f93fbd3af55230f5b32108e.tar.gz tinyrad-8f60a9558933e80d3f93fbd3af55230f5b32108e.tar.bz2 tinyrad-8f60a9558933e80d3f93fbd3af55230f5b32108e.tar.xz tinyrad-8f60a9558933e80d3f93fbd3af55230f5b32108e.zip | |
examples: adding initial socket example
Diffstat (limited to 'examples/socket-example.c')
| -rw-r--r-- | examples/socket-example.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/examples/socket-example.c b/examples/socket-example.c new file mode 100644 index 0000000..b372e8e --- /dev/null +++ b/examples/socket-example.c | |||
| @@ -0,0 +1,709 @@ | |||
| 1 | /* | ||
| 2 | * Tiny RADIUS Client Library | ||
| 3 | * Copyright (C) 2022 David M. Syzdek <david@syzdek.net>. | ||
| 4 | * All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions are | ||
| 8 | * met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in the | ||
| 14 | * documentation and/or other materials provided with the distribution. | ||
| 15 | * * Neither the name of David M. Syzdek nor the | ||
| 16 | * names of its contributors may be used to endorse or promote products | ||
| 17 | * derived from this software without specific prior written permission. | ||
| 18 | * | ||
| 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | ||
| 20 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||
| 21 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID M. SYZDEK BE LIABLE FOR | ||
| 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
| 29 | * SUCH DAMAGE. | ||
| 30 | */ | ||
| 31 | #define _SRC_SOCKET_EXAMPLE_C 1 | ||
| 32 | |||
| 33 | |||
| 34 | /////////////// | ||
| 35 | // // | ||
| 36 | // Headers // | ||
| 37 | // // | ||
| 38 | /////////////// | ||
| 39 | #pragma mark - Headers | ||
| 40 | |||
| 41 | #include <stdio.h> | ||
| 42 | #include <stdarg.h> | ||
| 43 | #include <stdlib.h> | ||
| 44 | #include <string.h> | ||
| 45 | #include <strings.h> | ||
| 46 | #include <unistd.h> | ||
| 47 | #include <getopt.h> | ||
| 48 | #include <arpa/inet.h> | ||
| 49 | #include <netdb.h> | ||
| 50 | #include <errno.h> | ||
| 51 | #include <fcntl.h> | ||
| 52 | #include <stdarg.h> | ||
| 53 | #include <poll.h> | ||
| 54 | |||
| 55 | #ifdef HAVE_CONFIG_H | ||
| 56 | #include "config.h" | ||
| 57 | #endif | ||
| 58 | |||
| 59 | |||
| 60 | /////////////////// | ||
| 61 | // // | ||
| 62 | // Definitions // | ||
| 63 | // // | ||
| 64 | /////////////////// | ||
| 65 | #pragma mark - Definitions | ||
| 66 | |||
| 67 | #undef PROGRAM_NAME | ||
| 68 | #define PROGRAM_NAME "socket-example" | ||
| 69 | #ifndef PACKAGE_BUGREPORT | ||
| 70 | # define PACKAGE_BUGREPORT "unknown" | ||
| 71 | #endif | ||
| 72 | #ifndef PACKAGE_COPYRIGHT | ||
| 73 | # define PACKAGE_COPYRIGHT "unknown" | ||
| 74 | #endif | ||
| 75 | #ifndef PACKAGE_NAME | ||
| 76 | # define PACKAGE_NAME "Tiny RADIUS Client Library" | ||
| 77 | #endif | ||
| 78 | #ifndef PACKAGE_VERSION | ||
| 79 | # define PACKAGE_VERSION "unknown" | ||
| 80 | #endif | ||
| 81 | |||
| 82 | #define MY_VERBOSE 0x0001 | ||
| 83 | #define MY_QUIET 0x0002 | ||
| 84 | #define MY_SERVER 0x0004 | ||
| 85 | #define MY_TCP 0x0008 | ||
| 86 | #define MY_UDP 0x0000 | ||
| 87 | |||
| 88 | #define MY_TIMEOUT 30 // in seconds | ||
| 89 | |||
| 90 | |||
| 91 | ///////////////// | ||
| 92 | // // | ||
| 93 | // Variables // | ||
| 94 | // // | ||
| 95 | ///////////////// | ||
| 96 | #pragma mark - Variables | ||
| 97 | |||
| 98 | int should_exit = 0; | ||
| 99 | |||
| 100 | |||
| 101 | ////////////////// | ||
| 102 | // // | ||
| 103 | // Prototypes // | ||
| 104 | // // | ||
| 105 | ////////////////// | ||
| 106 | #pragma mark - Prototypes | ||
| 107 | |||
| 108 | int main( int argc, char * argv[] ); | ||
| 109 | |||
| 110 | int my_client( int s, int opts, struct sockaddr_storage * sap ); | ||
| 111 | void my_connlog( int opts, const struct sockaddr_storage * sa, const char * fmt, ...); | ||
| 112 | int my_error( const char * fmt, ...); | ||
| 113 | char * my_ntop(const struct sockaddr_storage * sa, char * buff, size_t len); | ||
| 114 | void my_print( int opts, const char * fmt, ...); | ||
| 115 | int my_server( int s, int opts ); | ||
| 116 | int my_server_tcp( int s, int opts ); | ||
| 117 | int my_server_udp( int s, int opts ); | ||
| 118 | void my_signal( int sig ); | ||
| 119 | void my_usage( void ); | ||
| 120 | void my_verbose( int opts, const char * fmt, ...); | ||
| 121 | |||
| 122 | |||
| 123 | ///////////////// | ||
| 124 | // // | ||
| 125 | // Functions // | ||
| 126 | // // | ||
| 127 | ///////////////// | ||
| 128 | #pragma mark - Functions | ||
| 129 | |||
| 130 | int main(int argc, char * argv[]) | ||
| 131 | { | ||
| 132 | int c; | ||
| 133 | int opt_index; | ||
| 134 | int rc; | ||
| 135 | int s; | ||
| 136 | int opt; | ||
| 137 | unsigned opts; | ||
| 138 | const char * laddr; | ||
| 139 | const char * port; | ||
| 140 | struct addrinfo rinfo; | ||
| 141 | struct addrinfo hints; | ||
| 142 | struct addrinfo * res; | ||
| 143 | struct sockaddr_storage lsa; | ||
| 144 | struct sockaddr_storage sa; | ||
| 145 | char host[INET6_ADDRSTRLEN+INET_ADDRSTRLEN]; | ||
| 146 | |||
| 147 | // getopt options | ||
| 148 | static char short_opt[] = "46hl:p:qtVv"; | ||
| 149 | static struct option long_opt[] = | ||
| 150 | { | ||
| 151 | {"help", no_argument, NULL, 'h' }, | ||
| 152 | {"quiet", no_argument, NULL, 'q' }, | ||
| 153 | {"silent", no_argument, NULL, 'q' }, | ||
| 154 | {"tcp", no_argument, NULL, 't' }, | ||
| 155 | {"version", no_argument, NULL, 'V' }, | ||
| 156 | {"verbose", no_argument, NULL, 'v' }, | ||
| 157 | { NULL, 0, NULL, 0 } | ||
| 158 | }; | ||
| 159 | |||
| 160 | bzero(&hints, sizeof(struct addrinfo)); | ||
| 161 | rinfo.ai_family = PF_UNSPEC; | ||
| 162 | rinfo.ai_protocol = IPPROTO_UDP; | ||
| 163 | rinfo.ai_socktype = SOCK_DGRAM; | ||
| 164 | rinfo.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; | ||
| 165 | opts = 0; | ||
| 166 | laddr = NULL; | ||
| 167 | port = "1980"; | ||
| 168 | |||
| 169 | while((c = getopt_long(argc, argv, short_opt, long_opt, &opt_index)) != -1) | ||
| 170 | { | ||
| 171 | switch(c) | ||
| 172 | { | ||
| 173 | case -1: /* no more arguments */ | ||
| 174 | case 0: /* long options toggles */ | ||
| 175 | break; | ||
| 176 | |||
| 177 | case '4': | ||
| 178 | rinfo.ai_family = PF_INET; | ||
| 179 | break; | ||
| 180 | |||
| 181 | case '6': | ||
| 182 | rinfo.ai_family = PF_INET6; | ||
| 183 | break; | ||
| 184 | |||
| 185 | case 'h': | ||
| 186 | my_usage(); | ||
| 187 | return(0); | ||
| 188 | |||
| 189 | case 'l': | ||
| 190 | laddr = optarg; | ||
| 191 | break; | ||
| 192 | |||
| 193 | case 'p': | ||
| 194 | port = optarg; | ||
| 195 | break; | ||
| 196 | |||
| 197 | case 'q': | ||
| 198 | opts |= MY_QUIET; | ||
| 199 | opts &= ~MY_VERBOSE; | ||
| 200 | break; | ||
| 201 | |||
| 202 | case 't': | ||
| 203 | rinfo.ai_protocol = IPPROTO_TCP; | ||
| 204 | rinfo.ai_socktype = SOCK_STREAM; | ||
| 205 | opts |= MY_TCP; | ||
| 206 | break; | ||
| 207 | |||
| 208 | case 'V': | ||
| 209 | printf("%s (%s) %s\n", PROGRAM_NAME, PACKAGE_NAME, PACKAGE_VERSION); | ||
| 210 | return(0); | ||
| 211 | |||
| 212 | case 'v': | ||
| 213 | opts |= MY_VERBOSE; | ||
| 214 | opts &= ~MY_QUIET; | ||
| 215 | break; | ||
| 216 | |||
| 217 | case '?': | ||
| 218 | fprintf(stderr, "Try `%s --help' for more information.\n", PROGRAM_NAME); | ||
| 219 | return(1); | ||
| 220 | |||
| 221 | default: | ||
| 222 | fprintf(stderr, "%s: unrecognized option `--%c'\n", PROGRAM_NAME, c); | ||
| 223 | fprintf(stderr, "Try `%s --help' for more information.\n", PROGRAM_NAME); | ||
| 224 | return(1); | ||
| 225 | }; | ||
| 226 | }; | ||
| 227 | |||
| 228 | if ((optind + 1) < argc) | ||
| 229 | { | ||
| 230 | fprintf(stderr, "%s: unknown argument `%s'\n", PROGRAM_NAME, argv[optind+1]); | ||
| 231 | fprintf(stderr, "Try `%s --help' for more information.\n", PROGRAM_NAME); | ||
| 232 | return(1); | ||
| 233 | }; | ||
| 234 | |||
| 235 | if (optind == argc) | ||
| 236 | opts |= MY_SERVER; | ||
| 237 | |||
| 238 | // initializes signals | ||
| 239 | my_verbose(opts, "configuring signals"); | ||
| 240 | signal(SIGINT, my_signal); | ||
| 241 | signal(SIGQUIT, my_signal); | ||
| 242 | signal(SIGTERM, my_signal); | ||
| 243 | signal(SIGHUP, my_signal); | ||
| 244 | signal(SIGVTALRM, SIG_IGN); | ||
| 245 | signal(SIGPIPE, SIG_IGN); | ||
| 246 | signal(SIGALRM, SIG_IGN); | ||
| 247 | signal(SIGUSR1, SIG_IGN); | ||
| 248 | signal(SIGUSR2, SIG_IGN); | ||
| 249 | |||
| 250 | // resolve remote host | ||
| 251 | if ((optind + 1) == argc) | ||
| 252 | { | ||
| 253 | my_verbose(opts, "resolving remote host \"%s\" port \"%s\" ...", argv[optind], port); | ||
| 254 | res = NULL; | ||
| 255 | memcpy(&hints, &rinfo, sizeof(struct addrinfo)); | ||
| 256 | if ((rc = getaddrinfo(argv[optind], port, &hints, &res)) != 0) | ||
| 257 | { | ||
| 258 | my_error("getaddrinfo(\"%s\", \"%s\"): %s", argv[optind], port, gai_strerror(rc)); | ||
| 259 | return(1); | ||
| 260 | }; | ||
| 261 | port = NULL; | ||
| 262 | rinfo.ai_family = res->ai_family; | ||
| 263 | bzero(&sa, sizeof(sa)); | ||
| 264 | memcpy(&sa, res->ai_addr, res->ai_addrlen); | ||
| 265 | freeaddrinfo(res); | ||
| 266 | if (!(laddr)) | ||
| 267 | laddr = (sa.ss_family == AF_INET) ? "0.0.0.0" : "::"; | ||
| 268 | }; | ||
| 269 | |||
| 270 | // resolve local address | ||
| 271 | my_verbose(opts, "resolving local address \"%s\" port \"%s\"...", (((laddr)) ? laddr : "any"), (((port)) ? port : "any")); | ||
| 272 | memcpy(&hints, &rinfo, sizeof(struct addrinfo)); | ||
| 273 | res = NULL; | ||
| 274 | hints.ai_flags |= AI_NUMERICHOST | AI_PASSIVE | AI_V4MAPPED_CFG; | ||
| 275 | if ((rc = getaddrinfo(laddr, port, &hints, &res)) != 0) | ||
| 276 | { | ||
| 277 | my_error("getaddrinfo(\"%s\", \"%s\", ...): %s\n", (((laddr)) ? laddr : "NULL"), (((port)) ? port : "NULL"), gai_strerror(rc)); | ||
| 278 | return(1); | ||
| 279 | }; | ||
| 280 | bzero(&lsa, sizeof(lsa)); | ||
| 281 | memcpy(&lsa, res->ai_addr, res->ai_addrlen); | ||
| 282 | freeaddrinfo(res); | ||
| 283 | |||
| 284 | // create socket | ||
| 285 | my_verbose(opts, "opening %s socket ...", (hints.ai_socktype == SOCK_STREAM) ? "TCP" : ((hints.ai_socktype == SOCK_DGRAM) ? "UDP" : "unknown")); | ||
| 286 | if ((s = socket(lsa.ss_family, hints.ai_socktype, 0)) == -1) | ||
| 287 | { | ||
| 288 | my_error("socket(): %s\n", strerror(errno)); | ||
| 289 | return(1); | ||
| 290 | }; | ||
| 291 | |||
| 292 | // configures socket | ||
| 293 | my_verbose(opts, "configuring socket ..."); | ||
| 294 | opt = 1; setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(int)); | ||
| 295 | opt = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)); | ||
| 296 | opt = 1; setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void *)&opt, sizeof(int)); | ||
| 297 | opt = 1; | ||
| 298 | if (hints.ai_protocol == IPPROTO_TCP) | ||
| 299 | setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&opt, sizeof(int)); | ||
| 300 | |||
| 301 | // bind to local address | ||
| 302 | my_verbose(opts, "binding to \"%s\" ...", my_ntop(&lsa, host, sizeof(host))); | ||
| 303 | if (bind(s, (struct sockaddr *)&lsa, lsa.ss_len) == -1) | ||
| 304 | { | ||
| 305 | my_error("bind(): %s\n", strerror(errno)); | ||
| 306 | close(s); | ||
| 307 | return(1); | ||
| 308 | }; | ||
| 309 | |||
| 310 | return(((opts & MY_SERVER)) ? my_server(s, opts) : my_client(s, opts, &sa)); | ||
| 311 | } | ||
| 312 | |||
| 313 | |||
| 314 | int my_client( int s, int opts, struct sockaddr_storage * sap ) | ||
| 315 | { | ||
| 316 | struct pollfd fds[2]; | ||
| 317 | int rc; | ||
| 318 | struct sockaddr_storage sa; | ||
| 319 | socklen_t sa_len; | ||
| 320 | char buff[4096]; | ||
| 321 | ssize_t len; | ||
| 322 | |||
| 323 | fds[0].fd = s; | ||
| 324 | fds[0].events = POLLIN | POLLPRI | POLLRDBAND; | ||
| 325 | fds[0].revents = 0; | ||
| 326 | fds[1].fd = STDIN_FILENO; | ||
| 327 | fds[1].events = POLLIN | POLLPRI | POLLRDBAND; | ||
| 328 | fds[1].revents = 0; | ||
| 329 | |||
| 330 | // connects to remote host | ||
| 331 | my_verbose(opts, "connecting to \"%s\" ...", my_ntop(sap, buff, sizeof(buff))); | ||
| 332 | if (connect(s, (struct sockaddr *)sap, sap->ss_len) == -1) | ||
| 333 | { | ||
| 334 | my_error("connect(): %s\n", strerror(errno)); | ||
| 335 | close(s); | ||
| 336 | return(1); | ||
| 337 | }; | ||
| 338 | |||
| 339 | if ((opts & MY_VERBOSE)) | ||
| 340 | { | ||
| 341 | sa_len = sizeof(sa); | ||
| 342 | getpeername(s, (struct sockaddr *)&sa, &sa_len); | ||
| 343 | my_verbose(opts, "remote socket: %s", my_ntop(&sa, buff, sizeof(buff))); | ||
| 344 | sa_len = sizeof(sa); | ||
| 345 | getsockname(s, (struct sockaddr *)&sa, &sa_len); | ||
| 346 | my_verbose(opts, "local socket: %s", my_ntop(&sa, buff, sizeof(buff))); | ||
| 347 | }; | ||
| 348 | |||
| 349 | my_verbose(opts, "starting client loop"); | ||
| 350 | while (!(should_exit)) | ||
| 351 | { | ||
| 352 | // check for network data | ||
| 353 | if ((rc = poll(fds, 2, 1000)) == -1) | ||
| 354 | { | ||
| 355 | if (errno == EINTR) | ||
| 356 | continue; | ||
| 357 | my_error("poll(): %s", strerror(errno)); | ||
| 358 | my_verbose(opts, "closing socket ..."); | ||
| 359 | close(s); | ||
| 360 | return(1); | ||
| 361 | }; | ||
| 362 | |||
| 363 | // receives data | ||
| 364 | if ((fds[0].revents & POLLIN)) | ||
| 365 | { | ||
| 366 | fds[0].revents = 0; | ||
| 367 | sa_len = sizeof(sa); | ||
| 368 | if ((len = recvfrom(s, buff, sizeof(buff), 0, (struct sockaddr *)&sa, &sa_len)) == -1) | ||
| 369 | { | ||
| 370 | my_error("recvfrom(): %s", strerror(errno)); | ||
| 371 | close(s); | ||
| 372 | return(1); | ||
| 373 | }; | ||
| 374 | if (len == 0) | ||
| 375 | { | ||
| 376 | my_error("recvfrom(): zero bytes received"); | ||
| 377 | close(s); | ||
| 378 | return(1); | ||
| 379 | }; | ||
| 380 | buff[len] = '\0'; | ||
| 381 | my_connlog(opts, &sa, "received %i bytes", len); | ||
| 382 | printf(">>> %s\n", buff); | ||
| 383 | }; | ||
| 384 | |||
| 385 | if ((fds[0].revents & (POLLHUP | POLLERR | POLLNVAL))) | ||
| 386 | { | ||
| 387 | my_verbose(opts, "Connection closed by foreign host."); | ||
| 388 | close(s); | ||
| 389 | return(0); | ||
| 390 | }; | ||
| 391 | |||
| 392 | // read STDIN | ||
| 393 | if ((fds[1].revents & POLLIN)) | ||
| 394 | { | ||
| 395 | fds[1].revents = 0; | ||
| 396 | if ((len = read(STDIN_FILENO, buff, sizeof(buff))) == -1) | ||
| 397 | { | ||
| 398 | my_error("read(): %s", strerror(errno)); | ||
| 399 | close(s); | ||
| 400 | return(1); | ||
| 401 | }; | ||
| 402 | buff[len] = '\0'; | ||
| 403 | my_verbose(opts, "STDIN: read %i bytes", len); | ||
| 404 | if ((len = send(s, buff, strlen(buff), 0)) == -1) | ||
| 405 | { | ||
| 406 | my_error("send(): %s", strerror(errno)); | ||
| 407 | close(s); | ||
| 408 | return(1); | ||
| 409 | }; | ||
| 410 | }; | ||
| 411 | }; | ||
| 412 | |||
| 413 | my_verbose(opts, "closing socket ..."); | ||
| 414 | close(s); | ||
| 415 | |||
| 416 | return(0); | ||
| 417 | } | ||
| 418 | |||
| 419 | |||
| 420 | void my_connlog( int opts, const struct sockaddr_storage * sa, const char * fmt, ...) | ||
| 421 | { | ||
| 422 | va_list args; | ||
| 423 | char host[INET6_ADDRSTRLEN+32]; | ||
| 424 | |||
| 425 | if ((opts & MY_QUIET)) | ||
| 426 | return; | ||
| 427 | |||
| 428 | printf("%s: %s: ", PROGRAM_NAME, my_ntop(sa, host, sizeof(host))); | ||
| 429 | va_start(args, fmt); | ||
| 430 | vprintf(fmt, args); | ||
| 431 | va_end(args); | ||
| 432 | printf("\n"); | ||
| 433 | |||
| 434 | return; | ||
| 435 | } | ||
| 436 | |||
| 437 | |||
| 438 | int my_error( const char * fmt, ...) | ||
| 439 | { | ||
| 440 | va_list args; | ||
| 441 | fprintf(stderr, "%s: ", PROGRAM_NAME); | ||
| 442 | va_start(args, fmt); | ||
| 443 | vfprintf(stderr, fmt, args); | ||
| 444 | va_end(args); | ||
| 445 | fprintf(stderr, "\n"); | ||
| 446 | return(1); | ||
| 447 | } | ||
| 448 | |||
| 449 | |||
| 450 | char * my_ntop(const struct sockaddr_storage * sa, char * buff, size_t len) | ||
| 451 | { | ||
| 452 | char addrstr[INET6_ADDRSTRLEN]; | ||
| 453 | int port; | ||
| 454 | if (sa->ss_family == AF_INET) | ||
| 455 | { | ||
| 456 | inet_ntop(AF_INET, &((const struct sockaddr_in *)sa)->sin_addr, addrstr, sizeof(addrstr)); | ||
| 457 | port = ntohs(((const struct sockaddr_in *)sa)->sin_port); | ||
| 458 | snprintf(buff, len, "%s:%i", addrstr, port); | ||
| 459 | } else { | ||
| 460 | inet_ntop(AF_INET6, &((const struct sockaddr_in6 *)sa)->sin6_addr, addrstr, sizeof(addrstr)); | ||
| 461 | port = ntohs(((const struct sockaddr_in6 *)sa)->sin6_port); | ||
| 462 | snprintf(buff, len, "[%s]:%i", addrstr, port); | ||
| 463 | }; | ||
| 464 | buff[len-1] = '\0'; | ||
| 465 | return(buff); | ||
| 466 | } | ||
| 467 | |||
| 468 | |||
| 469 | void my_print( int opts, const char * fmt, ...) | ||
| 470 | { | ||
| 471 | va_list args; | ||
| 472 | if ((opts & MY_QUIET)) | ||
| 473 | return; | ||
| 474 | printf("%s: ", PROGRAM_NAME); | ||
| 475 | va_start(args, fmt); | ||
| 476 | vprintf(fmt, args); | ||
| 477 | va_end(args); | ||
| 478 | printf("\n"); | ||
| 479 | return; | ||
| 480 | } | ||
| 481 | |||
| 482 | |||
| 483 | int my_server( int s, int opts ) | ||
| 484 | { | ||
| 485 | struct sockaddr_storage sa; | ||
| 486 | socklen_t sa_len; | ||
| 487 | char buff[INET6_ADDRSTRLEN+INET6_ADDRSTRLEN]; | ||
| 488 | |||
| 489 | fcntl(s, F_SETFL, O_NONBLOCK); | ||
| 490 | |||
| 491 | sa_len = sizeof(sa); | ||
| 492 | getsockname(s, (struct sockaddr *)&sa, &sa_len); | ||
| 493 | my_print(opts, "starting %s server on \"%s\"", (((opts & MY_TCP)) ? "TCP" : "UDP"), my_ntop(&sa, buff, sizeof(buff))); | ||
| 494 | |||
| 495 | if ((opts & MY_TCP)) | ||
| 496 | return(my_server_tcp(s, opts)); | ||
| 497 | return(my_server_udp(s, opts)); | ||
| 498 | } | ||
| 499 | |||
| 500 | |||
| 501 | int my_server_tcp( int s, int opts ) | ||
| 502 | { | ||
| 503 | struct pollfd fds[10]; | ||
| 504 | nfds_t nfds; | ||
| 505 | nfds_t pos; | ||
| 506 | nfds_t x; | ||
| 507 | int rc; | ||
| 508 | struct sockaddr_storage sa; | ||
| 509 | struct sockaddr_storage sa_list[10]; | ||
| 510 | socklen_t sa_len; | ||
| 511 | int opt; | ||
| 512 | socklen_t opt_len; | ||
| 513 | char buff[4096]; | ||
| 514 | ssize_t len; | ||
| 515 | |||
| 516 | bzero(sa_list, sizeof(sa_list)); | ||
| 517 | fds[0].fd = s; | ||
| 518 | fds[0].events = POLLIN | POLLPRI | POLLRDBAND; | ||
| 519 | fds[0].revents = 0; | ||
| 520 | nfds = 1; | ||
| 521 | |||
| 522 | my_verbose(opts, "listening on socket"); | ||
| 523 | if (listen(s, 30) == -1) | ||
| 524 | { | ||
| 525 | my_error("listen(): %s", strerror(errno)); | ||
| 526 | close(s); | ||
| 527 | return(1); | ||
| 528 | }; | ||
| 529 | |||
| 530 | my_verbose(opts, "starting server loop"); | ||
| 531 | while ( (!(should_exit)) && ((nfds)) ) | ||
| 532 | { | ||
| 533 | // check for network data | ||
| 534 | if ((rc = poll(fds, nfds, (MY_TIMEOUT*1000))) == -1) | ||
| 535 | { | ||
| 536 | if (errno == EINTR) | ||
| 537 | continue; | ||
| 538 | my_error("poll(): %s", strerror(errno)); | ||
| 539 | my_verbose(opts, "closing sockets ..."); | ||
| 540 | for(pos = 0; (pos < nfds); pos++) | ||
| 541 | close(fds[pos].fd); | ||
| 542 | return(1); | ||
| 543 | }; | ||
| 544 | if (!(rc)) | ||
| 545 | continue; | ||
| 546 | |||
| 547 | // checks for new connections | ||
| 548 | if ((fds[0].revents & POLLIN)) | ||
| 549 | { | ||
| 550 | sa_len = sizeof(sa); | ||
| 551 | if ((s = accept(fds[0].fd, (struct sockaddr *)&sa, &sa_len)) == -1) | ||
| 552 | { | ||
| 553 | my_error("accept(): %s", strerror(errno)); | ||
| 554 | my_verbose(opts, "closing sockets ..."); | ||
| 555 | for(pos = 0; (pos < nfds); pos++) | ||
| 556 | close(fds[pos].fd); | ||
| 557 | return(1); | ||
| 558 | }; | ||
| 559 | my_connlog(opts, &sa, "new connection"); | ||
| 560 | if (nfds < 10) | ||
| 561 | { | ||
| 562 | memcpy(&sa_list[nfds], &sa, sizeof(struct sockaddr_storage)); | ||
| 563 | fds[nfds].fd = s; | ||
| 564 | fds[nfds].events = fds[0].events; | ||
| 565 | fds[nfds].revents = 0; | ||
| 566 | nfds++; | ||
| 567 | } else { | ||
| 568 | my_connlog(opts, &sa, "connection limit exceeded, closing"); | ||
| 569 | close(s); | ||
| 570 | }; | ||
| 571 | }; | ||
| 572 | |||
| 573 | // services client connections | ||
| 574 | for(pos = 1; (pos < nfds); pos++) | ||
| 575 | { | ||
| 576 | // closes socket | ||
| 577 | if ( ((fds[pos].revents & POLLERR)) || ((fds[pos].revents & POLLHUP)) ) | ||
| 578 | { | ||
| 579 | opt_len = sizeof(opt); | ||
| 580 | getsockopt(fds[pos].fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len); | ||
| 581 | my_connlog(opts, &sa_list[pos], "closing socket"); | ||
| 582 | close(fds[pos].fd); | ||
| 583 | for(x = pos+1; (x < nfds); x++) | ||
| 584 | { | ||
| 585 | memcpy(&sa_list[x-1], &sa_list[x], sizeof(struct sockaddr_storage)); | ||
| 586 | fds[x-1].fd = fds[x].fd; | ||
| 587 | }; | ||
| 588 | nfds--; | ||
| 589 | }; | ||
| 590 | |||
| 591 | // reads socket | ||
| 592 | if ((fds[pos].revents & POLLIN)) | ||
| 593 | { | ||
| 594 | if ((len = recv(fds[pos].fd, buff, sizeof(buff), 0)) > 0) | ||
| 595 | { | ||
| 596 | my_connlog(opts, &sa_list[pos], "received %i bytes", len); | ||
| 597 | snprintf(buff, sizeof(buff), "you sent %i bytes\n", (int)len); | ||
| 598 | if ((len = send(fds[pos].fd, buff, strlen(buff), 0)) > 0) | ||
| 599 | my_connlog(opts, &sa_list[pos], "sent %i bytes", len); | ||
| 600 | }; | ||
| 601 | }; | ||
| 602 | }; | ||
| 603 | }; | ||
| 604 | |||
| 605 | my_verbose(opts, "closing sockets ..."); | ||
| 606 | for(pos = 0; (pos < nfds); pos++) | ||
| 607 | close(fds[pos].fd); | ||
| 608 | |||
| 609 | return(0); | ||
| 610 | } | ||
| 611 | |||
| 612 | |||
| 613 | int my_server_udp( int s, int opts ) | ||
| 614 | { | ||
| 615 | struct pollfd fds[1]; | ||
| 616 | int rc; | ||
| 617 | struct sockaddr_storage sa; | ||
| 618 | socklen_t sa_len; | ||
| 619 | char buff[4096]; | ||
| 620 | ssize_t len; | ||
| 621 | |||
| 622 | fds[0].fd = s; | ||
| 623 | fds[0].events = POLLIN | POLLPRI | POLLRDBAND; | ||
| 624 | fds[0].revents = 0; | ||
| 625 | |||
| 626 | my_verbose(opts, "starting server loop"); | ||
| 627 | while (!(should_exit)) | ||
| 628 | { | ||
| 629 | // check for network data | ||
| 630 | if ((rc = poll(fds, 1, (MY_TIMEOUT*1000))) == -1) | ||
| 631 | { | ||
| 632 | if (errno == EINTR) | ||
| 633 | continue; | ||
| 634 | my_error("poll(): %s", strerror(errno)); | ||
| 635 | my_verbose(opts, "closing socket ..."); | ||
| 636 | close(s); | ||
| 637 | return(1); | ||
| 638 | }; | ||
| 639 | if (!(rc)) | ||
| 640 | continue; | ||
| 641 | |||
| 642 | // receives data | ||
| 643 | if ((fds[0].revents & POLLIN)) | ||
| 644 | { | ||
| 645 | sa_len = sizeof(sa); | ||
| 646 | if ((len = recvfrom(s, buff, sizeof(buff), 0, (struct sockaddr *)&sa, &sa_len)) == -1) | ||
| 647 | { | ||
| 648 | my_error("recvfrom(): %s", strerror(errno)); | ||
| 649 | close(s); | ||
| 650 | return(1); | ||
| 651 | }; | ||
| 652 | my_connlog(opts, &sa, "received %i bytes", len); | ||
| 653 | snprintf(buff, sizeof(buff), "you sent %i bytes\n", (int)len); | ||
| 654 | if ((len = sendto(s, buff, strlen(buff), 0, (struct sockaddr *)&sa, sa_len)) == -1) | ||
| 655 | { | ||
| 656 | my_error("sendto(): %s", strerror(errno)); | ||
| 657 | close(s); | ||
| 658 | return(1); | ||
| 659 | }; | ||
| 660 | my_connlog(opts, &sa, "sent %i bytes", len); | ||
| 661 | }; | ||
| 662 | }; | ||
| 663 | |||
| 664 | my_verbose(opts, "closing socket ..."); | ||
| 665 | close(s); | ||
| 666 | |||
| 667 | return(0); | ||
| 668 | } | ||
| 669 | |||
| 670 | |||
| 671 | void my_signal( int sig ) | ||
| 672 | { | ||
| 673 | signal(sig, my_signal); | ||
| 674 | should_exit = 1; | ||
| 675 | my_verbose(MY_VERBOSE, "caught signal"); | ||
| 676 | return; | ||
| 677 | } | ||
| 678 | |||
| 679 | |||
| 680 | void my_usage(void) | ||
| 681 | { | ||
| 682 | printf("Usage: %s [OPTIONS] url url ... url\n", PROGRAM_NAME); | ||
| 683 | printf("Options:\n"); | ||
| 684 | printf(" -4 use IPv4 addresses\n"); | ||
| 685 | printf(" -6 use IPv6 addresses\n"); | ||
| 686 | printf(" -h, --help print this help and exit\n"); | ||
| 687 | printf(" -l address local address\n"); | ||
| 688 | printf(" -q, --quiet, --silent do not print messages\n"); | ||
| 689 | printf(" --tcp, -t use TCP/IP\n"); | ||
| 690 | printf(" -V, --version print version number and exit\n"); | ||
| 691 | printf(" -v, --verbose print verbose messages\n"); | ||
| 692 | printf("\n"); | ||
| 693 | return; | ||
| 694 | } | ||
| 695 | |||
| 696 | void my_verbose( int opts, const char * fmt, ...) | ||
| 697 | { | ||
| 698 | va_list args; | ||
| 699 | if (!(opts & MY_VERBOSE)) | ||
| 700 | return; | ||
| 701 | printf("%s: ", PROGRAM_NAME); | ||
| 702 | va_start(args, fmt); | ||
| 703 | vprintf(fmt, args); | ||
| 704 | va_end(args); | ||
| 705 | printf("\n"); | ||
| 706 | return; | ||
| 707 | } | ||
| 708 | |||
| 709 | /* end of source */ | ||
