lördag den 19 februari 2011 klockan 22:30 skrev Michael Meskes detta: > On Tue, Feb 15, 2011 at 02:37:53PM +0100, Mats Erik Andersson wrote: > > You might be interested in hearing that I have a new > > patch in testing, a patch that does support IPv6. > > Would you mind sharing this patch with us so we can test it, too?
Here it comes! Having detected the serious fault in #613557, a portion of self-esteem dictated that a corresponding solution first be found, in order that my IPv6 patch should not be blamed for that error. Thus arose a week's delay. The patch included here is computed on top of the changes implemented for the resolution of #613557. As of now I have left the code at a stage using the first address returned by getaddrinfo(3), not implementing a full queueing of all permissible addresses deliverable from the lookup. The new options '-4' and '-6' influence an a priory decision on a specific domain family. Best regards, Mats Erik Andersson
Description: Make changes for IPv6 support. The migration to code using getaddrinfo(3) is readily able to support IPv6 for Strobe. The subroutine resolve() must be eliminated in order to achieve bookeeping needed for portable code. The present changes are func- tional for GNU/Linux, GNU/kFreeBSD, OpenBSD, and FreeBSD. . A numerical target host, supplemented with a port, or port range, must use brackets for the numerical IPv6 address: . strobe [::1]:20-90 . but brackets are optional for IPv6 without a port specification. . Presently the implementation applies the first acceptable address returned from getaddrinfo(3), but use of the new options '-4' or '-6' will restrict probing to a single domain family. Author: Mats Erik Andersson <deb...@gisladisker.se> Forwarded: no License: Simplified, two-clause BSD. Last-Update: 2011-02-21 diff -Naurp strobe.debian2/strobe.1 strobe/strobe.1 --- strobe.debian2/strobe.1 2011-02-18 12:32:14.000000000 +0100 +++ strobe/strobe.1 2011-02-21 22:59:18.000000000 +0100 @@ -45,6 +45,13 @@ Useful for automated output parsing. .B \-d Delete duplicate entries for port descriptions. i.e use only the first definition. .TP +.B \-4 +Restrict to IPv4 addresses. Default behaviour is to take the first address resolved for a host, +be it IPv4, or IPv6. +.TP +.B \-6 +Use only IPv6 addresses. +.TP .B \-g Disable usage of .BR getpeername (2). diff -Naurp strobe.debian2/strobe.c strobe/strobe.c --- strobe.debian2/strobe.c 2011-02-19 00:18:57.000000000 +0100 +++ strobe/strobe.c 2011-02-21 23:33:32.000000000 +0100 @@ -36,10 +36,6 @@ extern char *optarg; #define FALSE 0 #define TRUE 1 -#ifndef INADDR_NONE -# define INADDR_NONE ((unsigned long)-1) -#endif - #define port_t (unsigned short) /* @@ -66,14 +62,15 @@ int a_start = 1; int a_end = 65535; int a_sock_max = 64; int a_abort = 0; -int a_bindport = 0; +char *a_bindport = NULL; int a_capture = 1024; int a_wrap = 79; int a_lines = 1; char *a_bindaddr = NULL; char *a_dircap = NULL; char *a_views = "all,hosts,networks,ports"; -struct in_addr bindaddr; +struct sockaddr_storage *bindaddr = NULL; +socklen_t bindlen = 0; bool f_linear = 0; bool f_verbose = 0; bool f_verbose_stats = 0; @@ -85,6 +82,7 @@ bool f_minimise = 0; bool f_dontgetpeername = 0; bool f_hexdump = 0; +int use_family = AF_UNSPEC; int connects = 0; int hosts_done = 0; int attempts_done = 0; @@ -114,7 +112,12 @@ char *capture_buf; struct hosts_s { char *name; - struct in_addr in_addr; + struct sockaddr_storage ss; + socklen_t addrlen; /* Important for BSD. */ + union { + struct in_addr in_addr; + struct in6_addr in6_addr; + } addr; int port; int *portlist; int portlist_n,portlist_alloc; @@ -138,7 +141,12 @@ struct hosts_s *hosts; struct htuple_s { char *name; - struct in_addr in_addr; + struct sockaddr_storage ss; + socklen_t addrlen; /* Important for BSD. */ + union { + struct in_addr in_addr; + struct in6_addr in6_addr; + } addr; int port; int sfd; int status; @@ -306,12 +314,12 @@ int sc_connect (h) struct htuple_s *h; { - struct sockaddr_in sa_in; + struct sockaddr_storage ss; int sopts1 = 1; struct linger slinger; - if ((h->sfd = socket (PF_INET, SOCK_STREAM, 0)) == -1) + if ((h->sfd = socket (h->ss.ss_family, SOCK_STREAM, 0)) == -1) return 0; - memset(&sa_in, 0, sizeof(sa_in)); + memset(&ss, 0, sizeof(ss)); h->status |= HT_SOCKET; gettimeofday(&(h->sock_start), NULL); sock_unblock (h->sfd); @@ -319,25 +327,32 @@ sc_connect (h) setsockopt (h->sfd, SOL_SOCKET, SO_OOBINLINE, (char *) &sopts1, sizeof (sopts1)); slinger.l_onoff = 0; /* off */ setsockopt (h->sfd, SOL_SOCKET, SO_LINGER, (char *) &slinger, sizeof (slinger)); - sa_in.sin_family = AF_INET; - if (a_bindport) - sa_in.sin_port = a_bindport; - if (a_bindaddr) - sa_in.sin_addr = bindaddr; - if (a_bindaddr || a_bindport) - if (bind (h->sfd, (struct sockaddr *)&sa_in, sizeof(sa_in)) == -1) + if (bindaddr) + if (bind (h->sfd, (struct sockaddr *) bindaddr, bindlen) == -1) { - fprintf(stderr, "couldn't bind %s : %d ", a_bindaddr? a_bindaddr: "0.0.0.0", ntohs(a_bindport)); + fprintf(stderr, "could not bind to %s : %s ", + a_bindaddr ? a_bindaddr : "ANY", + a_bindport ? a_bindport : "0"); perror(""); if (errno == EACCES) exit(1); return 0; } - sa_in.sin_addr = h->in_addr; - sa_in.sin_port = htons (h->port); + + memcpy(&ss, &h->ss, h->addrlen); + switch (h->ss.ss_family) + { + case AF_INET6: + ((struct sockaddr_in6 *) &ss)->sin6_port = htons (h->port); + break; + case AF_INET: + default: + ((struct sockaddr_in *) &ss)->sin_port = htons (h->port); + break; + } h->host->attempts++; - if (connect (h->sfd, (struct sockaddr *) &sa_in, sizeof (sa_in)) == -1) + if (connect (h->sfd, (struct sockaddr *) &ss, h->addrlen) == -1) { switch (errno) { @@ -635,8 +650,8 @@ display_port_sw (h) if (a_dircap) { char buf[2048]; - char in[64]; - char *p; + char in[64], in_orig[64]; + char *p, *q; struct port_desc_s *pd; sprintf(master, "%.512s/strobe.%d", a_dircap, (int)getpid()); fh = fopen(master, "w"); @@ -650,50 +665,97 @@ display_port_sw (h) return; } } - strcpy(in, inet_ntoa(h->host->in_addr)); - for (p=in; *p; p++) - if (*p == '.') - *p = '/'; + + /* The originally resolved host address goes into IN_ORIG, + * while the string parsed as file path goes into IN. */ + in[0] = in_orig[0] = '\0'; + getnameinfo((struct sockaddr *) &h->host->ss, h->host->addrlen, + in_orig, sizeof(in_orig), NULL, 0, NI_NUMERICHOST); + + /* Address to path translation: + * + * IPv4: . --> / + * IPv6: :: --> /_/ + * : --> / + */ + p = in_orig; + q = in; + + if (*p == ':') + { + /* Initial colon in an IPv6 address. Caution! */ + ++p; + *q++ = '_'; + if (*p == ':') + { + ++p; + *q++ = '/'; + } + } + + for (; *p; ++p, ++q) + { + switch (*p) { + case '.': + *q = '/'; + break; + case ':': + if (*(p - 1) == ':') /* Already inside the string. */ + *q++ = '_'; + *q = '/'; + break; + default: + *q = *p; + } + } + *q = '\0'; /* End properly the parsed path. */ + pd = port_descs[h->port]; if (strstr(a_views, "networks")) { if (f_minimise) - sprintf(buf, "%.512s/networks/%s/%s-%.256s/%05d", a_dircap, in, inet_ntoa(h->host->in_addr), - h->host->name, h->port); + sprintf(buf, "%.512s/networks%s/%s/%s-%.256s/%05d", a_dircap, + (h->ss.ss_family == AF_INET6) ? "/ipv6" : "", + in, in_orig, h->host->name, h->port); else - sprintf(buf, "%.512s/networks/%s/%s-%.256s/%05d-%.200s-%.200s", a_dircap, in, inet_ntoa(h->host->in_addr), - h->host->name, h->port, pd? port_descs[h->port]->portname: "unknown", pd? trslash(port_descs[h->port]->name): ""); + sprintf(buf, "%.512s/networks%s/%s/%s-%.256s/%05d-%.200s-%.200s", a_dircap, + (h->ss.ss_family == AF_INET6) ? "/ipv6" : "", + in, in_orig, h->host->name, h->port, + pd ? port_descs[h->port]->portname : "unknown", + pd ? trslash(port_descs[h->port]->name) : ""); makeln(master, buf); } if (strstr(a_views, "hosts")) { if (f_minimise) - sprintf(buf, "%.512s/hosts/%s-%.256s/%05d", a_dircap, inet_ntoa(h->host->in_addr), + sprintf(buf, "%.512s/hosts/%s-%.256s/%05d", a_dircap, in_orig, h->host->name, h->port); else - sprintf(buf, "%.512s/hosts/%s-%.256s/%05d-%.200s-%.200s", a_dircap, inet_ntoa(h->host->in_addr), - h->host->name, h->port, pd? port_descs[h->port]->portname: "unknown", pd? trslash(port_descs[h->port]->name): ""); + sprintf(buf, "%.512s/hosts/%s-%.256s/%05d-%.200s-%.200s", a_dircap, in_orig, + h->host->name, h->port, pd? port_descs[h->port]->portname: "unknown", + pd? trslash(port_descs[h->port]->name): ""); makeln(master, buf); } if (strstr(a_views, "all")) { if (f_minimise) - sprintf(buf, "%.512s/all/%.64s-%.200s-%05d", a_dircap, inet_ntoa(h->host->in_addr), + sprintf(buf, "%.512s/all/%.64s-%.200s-%05d", a_dircap, in_orig, h->host->name, h->port); else - sprintf(buf, "%.512s/all/%.64s-%.200s-%05d-%.200s-%.200s", a_dircap, inet_ntoa(h->host->in_addr), - h->host->name, h->port, pd? port_descs[h->port]->portname: "unknown", pd? trslash(port_descs[h->port]->name): ""); + sprintf(buf, "%.512s/all/%.64s-%.200s-%05d-%.200s-%.200s", a_dircap, in_orig, + h->host->name, h->port, pd? port_descs[h->port]->portname: "unknown", + pd? trslash(port_descs[h->port]->name): ""); makeln(master, buf); } if (strstr(a_views, "ports")) { if (f_minimise) - sprintf(buf, "%.512s/ports/%05d/%s-%.200s", a_dircap, h->port, inet_ntoa(h->host->in_addr), + sprintf(buf, "%.512s/ports/%05d/%s-%.200s", a_dircap, h->port, in_orig, h->host->name); else sprintf(buf, "%.512s/ports/%05d-%.200s-%.200s/%.64s-%.200s", a_dircap, h->port, pd? port_descs[h->port]->portname: "unknown", pd? trslash(port_descs[h->port]->name): "", - inet_ntoa(h->host->in_addr), h->host->name); + in_orig, h->host->name); makeln(master, buf); } } @@ -845,7 +907,7 @@ gather (timeout_secs) if (FD_ISSET (h->sfd, &set_sel_r) || FD_ISSET (h->sfd, &set_sel_w)) { - struct sockaddr_in in; + struct sockaddr_storage in; socklen_t len = sizeof (in); selected--; @@ -941,7 +1003,9 @@ scatter (host, timeout) add = ht_initial; add.host = host; add.name = host->name; - add.in_addr = host->in_addr; + memcpy(&add.ss, &host->ss, sizeof(add.ss)); + add.addrlen = host->addrlen; + memcpy(&add.addr, &host->addr, sizeof(add.addr)); add.port = host->port; add.timeout = timeout; if (f_verbose) @@ -972,27 +1036,6 @@ wait_end (t) } } -struct in_addr -resolve (name) - char *name; -{ - static struct in_addr in; - unsigned long l; - struct hostent *ent; - if ((l = inet_addr (name)) != INADDR_NONE) - { - in.s_addr = l; - return in; - } - if (!(ent = gethostbyname (name))) - { - fprintf(stderr, "Host \"%s\" does not exist.\n", name); - in.s_addr = INADDR_NONE; - return in; - } - return *(struct in_addr *) ent->h_addr; -} - char * next_host () { @@ -1064,11 +1107,35 @@ host_init (h, name, nocheck) char *name; bool nocheck; { - int n; - char *ports; + struct addrinfo hints, *ai, *res; + int n, err; + char *ports, *q; *h=ho_initial; - if ((ports=strchr(name,':'))) + q = strchr(name, ':'); + ports = strrchr(name, ':'); + + /* Extract an address within brackets. */ + if (name[0] == '[') + { + ++name; + ports = strchr(name, ']'); + if (ports) + { + /* End the host address at ']'. */ + *ports = '\0'; + /* Is there a continuation? */ + ++ports; + if (*ports == ':') + q = ports; /* Arrange for later port parsing. */ + } else + /* No proper ending. */ + ports = NULL; + } + + /* A single colon indicates a port specifier in IPv4, + * or with a symbolic host name. */ + if (ports && (ports == q)) { char *pstart, *minus; int i, stopnow=0; @@ -1089,7 +1156,7 @@ host_init (h, name, nocheck) if ((minus=strchr(pstart,'-'))) { *minus=0; - lastport=atoi(minus++); + lastport=atoi(++minus); for(i=atoi(pstart);i<=lastport;i++) if (!add_port(h,i)) @@ -1120,20 +1187,82 @@ host_init (h, name, nocheck) exit(1); } } - h->in_addr = resolve (name); - if (h->in_addr.s_addr == INADDR_NONE) + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = use_family; +# ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +# endif + + if (f_verbose) + fprintf(stderr, "Resolving \"%s\".\n", name); + + err = getaddrinfo(name, NULL, &hints, &res); + if (err) + { + fprintf(stderr, "%s: %s\n", name, gai_strerror(err)); return 0; - if (!nocheck) - for (n=0; n<a_sock_max; n++) - { - if (hosts[n].name && hosts[n].in_addr.s_addr==h->in_addr.s_addr) - { - if (!f_quiet) - fprintf(stderr, "ip duplication: %s == %s (last host ignored)\n", - hosts[n].name, name); - return 0; + } + + for (ai = res; ai; ai = ai->ai_next) + { + int fd = -1, duplicate = 0; + + if ((fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP)) < 0) + continue; + + close(fd); + + memcpy(&h->ss, ai->ai_addr, ai->ai_addrlen); + h->addrlen = ai->ai_addrlen; + memset(&h->addr, 0, sizeof(h->addr)); + switch (ai->ai_family) + { + case AF_INET6: + memcpy(&h->addr.in6_addr, + &((struct sockaddr_in6 *) ai->ai_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; + case AF_INET: + default: + memcpy(&h->addr.in_addr, + &((struct sockaddr_in *) ai->ai_addr)->sin_addr, + sizeof(struct in_addr)); + } + + if (!nocheck) + for (n=0; n<a_sock_max; n++) + { + if ( hosts[n].name && (hosts[n].ss.ss_family == h->ss.ss_family) + && (memcmp(&hosts[n].addr, &h->addr, sizeof(h->addr)) == 0) ) + { + if (!f_quiet) + fprintf(stderr, "ip duplication: %s == %s (last host ignored)\n", + hosts[n].name, name); + duplicate = 1; + break; + } } - } + + if (duplicate) + continue; + + if (f_verbose) + fprintf(stderr, "Resolved \"%s\" as %s.\n", name, + (ai->ai_family == AF_INET6) ? "IPv6" : "IPv4"); + + /* Accept this address. + * TODO: Queue all addresses for the given host name. + */ + break; + } + + if (res) + freeaddrinfo(res); + + if (ai == NULL) + return 0; + h->name = (char *) Smalloc (strlen (name) + 1); strcpy (h->name, name); if(f_fast || h->portlist) @@ -1367,6 +1496,7 @@ usage () { fprintf (stderr, "\ usage: %8s [options]\n\ +\t\t[-4 | -6]\n\ \t\t[-v(erbose)]\n\ \t\t[-V(erbose_stats]\n\ \t\t[-m(inimise)]\n\ @@ -1406,9 +1536,15 @@ main (argc, argv) Argc = argc; Argv = argv; - while ((c = getopt (argc, argv, "o:dvVmgb:e:p:P:a:A:t:n:S:i:lfsqMc:w:xL:D:T:")) != -1) + while ((c = getopt (argc, argv, "h46o:dvVmgb:e:p:P:a:A:t:n:S:i:lfsqMc:w:xL:D:T:")) != -1) switch (c) { + case '4': + use_family = AF_INET; + break; + case '6': + use_family = AF_INET6; + break; case 'o': a_output = optarg; break; @@ -1434,16 +1570,10 @@ main (argc, argv) a_end = atoi (optarg); break; case 'P': - a_bindport = htons (atoi (optarg)); + a_bindport = optarg; break; case 'A': a_bindaddr = optarg; - bindaddr = resolve (a_bindaddr); - if (bindaddr.s_addr == INADDR_NONE) - { - perror(a_bindaddr); - exit(1); - } break; case 'p': a_start = a_end = atoi (optarg); @@ -1500,6 +1630,7 @@ main (argc, argv) case '?': default: fprintf (stderr, "unknown option %s\n", argv[optind-1]); + case 'h': usage (); /* NOT_REACHED */ } @@ -1514,6 +1645,36 @@ main (argc, argv) /* Mark the file descriptor as invalid. */ ht_initial.sfd = -1; + /* Make an early check on bind address and port. + * This saves work later on, and gives an error + * immediately. */ + if (a_bindport || a_bindaddr) + { + struct addrinfo hints, *res; + int err; + + bindaddr = (struct sockaddr_storage *) Smalloc(sizeof(*bindaddr)); + memset(bindaddr, 0, sizeof(*bindaddr)); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = use_family; + err = getaddrinfo(a_bindaddr, a_bindport, &hints, &res); + if (err) + { + fprintf(stderr, "Binding to %s : %s failed: %s\n", + a_bindaddr ? a_bindaddr : "ANY", + a_bindport ? a_bindport : "0", + gai_strerror(err)); + exit(1); + } + /* Only the first resolved address is used. + * TODO: Is a lookup preconditioned on family possible, + * beyond the present use of USE_FAMILY? + */ + bindlen = res->ai_addrlen; + memcpy(bindaddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } + if (a_input) { if ( ! strcmp("-",a_input) ) { /* Use stdin as input file */
signature.asc
Description: Digital signature