aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/examples/socket-example.c
diff options
context:
space:
mode:
authorDavid M. Syzdek <david@syzdek.net>2022-01-13 17:04:01 -0900
committerDavid M. Syzdek <david@syzdek.net>2022-01-13 20:48:03 -0900
commit8f60a9558933e80d3f93fbd3af55230f5b32108e (patch)
tree736b3bc8f067287d2616a62f7873d888be00de50 /examples/socket-example.c
parente3c2a2f50e8ee834296aec7fd6cdf96475907591 (diff)
downloadtinyrad-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.c709
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
98int should_exit = 0;
99
100
101//////////////////
102// //
103// Prototypes //
104// //
105//////////////////
106#pragma mark - Prototypes
107
108int main( int argc, char * argv[] );
109
110int my_client( int s, int opts, struct sockaddr_storage * sap );
111void my_connlog( int opts, const struct sockaddr_storage * sa, const char * fmt, ...);
112int my_error( const char * fmt, ...);
113char * my_ntop(const struct sockaddr_storage * sa, char * buff, size_t len);
114void my_print( int opts, const char * fmt, ...);
115int my_server( int s, int opts );
116int my_server_tcp( int s, int opts );
117int my_server_udp( int s, int opts );
118void my_signal( int sig );
119void my_usage( void );
120void my_verbose( int opts, const char * fmt, ...);
121
122
123/////////////////
124// //
125// Functions //
126// //
127/////////////////
128#pragma mark - Functions
129
130int 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
314int 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
420void 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
438int 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
450char * 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
469void 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
483int 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
501int 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
613int 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
671void 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
680void 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
696void 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 */