Re: dig(1): replace inet_net_pton(3)
On Wed, Jan 20, 2021 at 11:38:40AM +0100, Claudio Jeker wrote: > On Tue, Jan 19, 2021 at 07:49:29PM +0100, Florian Obser wrote: > > When we converted isc_sockaddr_t to sockaddr_storage we also moved to > > inet_net_pton(3). It turns out that was a mistake, at least it's not > > portable for AF_INET6. Effectively revert that part and hand-roll it > > using inet_pton(3). > > > > OK? > > I thought inet_net_pton() for AF_INET would still be ok. Handling the Or we haven't found the problems with AF_INET yet. The code in lib/libc/net/inet_net_pton is rather grotesque. The semantics are also mind-boggingly stupid. All numbers supplied as "parts" in a `.' notation may be decimal, octal, or hexadecimal, as specified in the C language (i.e., a leading 0x or 0X implies hexadecimal; otherwise, a leading 0 implies octal; otherwise, the number is interpreted as decimal). "0x0a" is valid and is 10.0.0.0/8 "0x0a0b0c0d" is valid and is 10.11.12.13/32 "0xC0" is valid and is 192.0.0.0/24 because of course the damn thing understands about network classes. > AF_INET case by hand here seems rather unpleasant. > > Since this code uses sockaddr to store the result why not use > getaddrinfo(). > > In bgpd the code does more or less this: > 1. extract the /prefixlen like you do. > 2. use getnameinfo() on the prefix > 3. if that fails use inet_net_pton(AF_INET) to parse short forms (like 10/8) I think inet_net_pton plain needs killing. If we were to agree that a sensible way to write IPv4 addresses is to use decimal numbers in the range 0-255 seperated by dots (allowing short form) we could easily write an AF-independent replacement using getnameinfo. Nobody uses this except for us it seems. Ports is clean, I found one hit in the FreeBSD base system in sbin/pfctl/pfctl_parser using only AF_INET. NetBSD additionally enables it in ftpd. Anyway, not a hill I'm going to die on. > > Note: I think the bgpd code could also use some further cleanup. > > I'm not sure if it makes sense to force this code to be portable by not > using inet_net_pton(3). The code is currently non-portable because it uses > the len fields of the sockaddr structs. So why bother about this function? I'm restoring previous behaviour. > In general if we plan to make a portable version of our dig the framework > can provide a working inet_net_pton(3) version. > > > p.s. it is kinda telling that isc, who introduced the API is (no > > longer?) using it. > > > > diff --git dighost.c dighost.c > > index 2d2a52c86e2..2995b7e1602 100644 > > --- dighost.c > > +++ dighost.c > > @@ -935,10 +935,16 @@ parse_netprefix(struct sockaddr_storage **sap, int > > *plen, const char *value) { > > struct sockaddr_storage *sa = NULL; > > struct in_addr *in4; > > struct in6_addr *in6; > > - int prefix_length; > > + int prefix_length = -1, i; > > + char *sep; > > + char buf[INET6_ADDRSTRLEN + sizeof("/128")]; > > + const char *errstr; > > > > REQUIRE(sap != NULL && *sap == NULL); > > > > + if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) > > + fatal("invalid prefix '%s'\n", value); > > + > > sa = calloc(1, sizeof(*sa)); > > if (sa == NULL) > > fatal("out of memory"); > > @@ -952,14 +958,36 @@ parse_netprefix(struct sockaddr_storage **sap, int > > *plen, const char *value) { > > goto done; > > } > > > > - if ((prefix_length = inet_net_pton(AF_INET6, value, in6, sizeof(*in6))) > > - != -1) { > > + sep = strchr(buf, '/'); > > + if (sep != NULL) { > > + *sep++ = '\0'; > > + prefix_length = strtonum(sep, 0, 128, &errstr); > > + if (errstr != NULL) > > + fatal("invalid address '%s'", value); > > + } > > + > > + if (inet_pton(AF_INET6, buf, in6) == 1) { > > sa->ss_len = sizeof(struct sockaddr_in6); > > sa->ss_family = AF_INET6; > > - } else if ((prefix_length = inet_net_pton(AF_INET, value, in4, > > - sizeof(*in4))) != -1) { > > + if (prefix_length > 128 || prefix_length == -1) > > prefix_length can't be bigger than 128. But I see why it makes sense to do > the same here :) > > > + prefix_length = 128; > > + } else if (inet_pton(AF_INET, buf, in4) == 1) { > > sa->ss_len = sizeof(struct sockaddr_in); > > sa->ss_family = AF_INET; > > + if (prefix_length > 32 || prefix_length == -1) > > + prefix_length = 32; > > In my opinion 10.0.0.0/75 is not a valid address and should not be > coerced to 10.0.0.0. This should result in an error. > > > + } else if (prefix_length != -1) { > > + if (prefix_length > 32) > > + prefix_length = 32; > > Same here. > > > + for (i = 0; i < 3 ; i++) { > > + if (strlcat(buf, ".0", sizeof(buf)) > sizeof(buf)) > > + fatal("invalid address '%s'", value); > > + if (inet_pton(AF_
Re: dig(1): replace inet_net_pton(3)
On Tue, Jan 19, 2021 at 07:49:29PM +0100, Florian Obser wrote: > When we converted isc_sockaddr_t to sockaddr_storage we also moved to > inet_net_pton(3). It turns out that was a mistake, at least it's not > portable for AF_INET6. Effectively revert that part and hand-roll it > using inet_pton(3). > > OK? I thought inet_net_pton() for AF_INET would still be ok. Handling the AF_INET case by hand here seems rather unpleasant. Since this code uses sockaddr to store the result why not use getaddrinfo(). In bgpd the code does more or less this: 1. extract the /prefixlen like you do. 2. use getnameinfo() on the prefix 3. if that fails use inet_net_pton(AF_INET) to parse short forms (like 10/8) Note: I think the bgpd code could also use some further cleanup. I'm not sure if it makes sense to force this code to be portable by not using inet_net_pton(3). The code is currently non-portable because it uses the len fields of the sockaddr structs. So why bother about this function? In general if we plan to make a portable version of our dig the framework can provide a working inet_net_pton(3) version. > p.s. it is kinda telling that isc, who introduced the API is (no > longer?) using it. > > diff --git dighost.c dighost.c > index 2d2a52c86e2..2995b7e1602 100644 > --- dighost.c > +++ dighost.c > @@ -935,10 +935,16 @@ parse_netprefix(struct sockaddr_storage **sap, int > *plen, const char *value) { > struct sockaddr_storage *sa = NULL; > struct in_addr *in4; > struct in6_addr *in6; > - int prefix_length; > + int prefix_length = -1, i; > + char *sep; > + char buf[INET6_ADDRSTRLEN + sizeof("/128")]; > + const char *errstr; > > REQUIRE(sap != NULL && *sap == NULL); > > + if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) > + fatal("invalid prefix '%s'\n", value); > + > sa = calloc(1, sizeof(*sa)); > if (sa == NULL) > fatal("out of memory"); > @@ -952,14 +958,36 @@ parse_netprefix(struct sockaddr_storage **sap, int > *plen, const char *value) { > goto done; > } > > - if ((prefix_length = inet_net_pton(AF_INET6, value, in6, sizeof(*in6))) > - != -1) { > + sep = strchr(buf, '/'); > + if (sep != NULL) { > + *sep++ = '\0'; > + prefix_length = strtonum(sep, 0, 128, &errstr); > + if (errstr != NULL) > + fatal("invalid address '%s'", value); > + } > + > + if (inet_pton(AF_INET6, buf, in6) == 1) { > sa->ss_len = sizeof(struct sockaddr_in6); > sa->ss_family = AF_INET6; > - } else if ((prefix_length = inet_net_pton(AF_INET, value, in4, > - sizeof(*in4))) != -1) { > + if (prefix_length > 128 || prefix_length == -1) prefix_length can't be bigger than 128. But I see why it makes sense to do the same here :) > + prefix_length = 128; > + } else if (inet_pton(AF_INET, buf, in4) == 1) { > sa->ss_len = sizeof(struct sockaddr_in); > sa->ss_family = AF_INET; > + if (prefix_length > 32 || prefix_length == -1) > + prefix_length = 32; In my opinion 10.0.0.0/75 is not a valid address and should not be coerced to 10.0.0.0. This should result in an error. > + } else if (prefix_length != -1) { > + if (prefix_length > 32) > + prefix_length = 32; Same here. > + for (i = 0; i < 3 ; i++) { > + if (strlcat(buf, ".0", sizeof(buf)) > sizeof(buf)) > + fatal("invalid address '%s'", value); > + if (inet_pton(AF_INET, buf, in4) == 1) { > + sa->ss_len = sizeof(struct sockaddr_in); > + sa->ss_family = AF_INET; > + goto done; > + } > + } > } else > fatal("invalid address '%s'", value); > -- :wq Claudio
dig(1): replace inet_net_pton(3)
When we converted isc_sockaddr_t to sockaddr_storage we also moved to inet_net_pton(3). It turns out that was a mistake, at least it's not portable for AF_INET6. Effectively revert that part and hand-roll it using inet_pton(3). OK? p.s. it is kinda telling that isc, who introduced the API is (no longer?) using it. diff --git dighost.c dighost.c index 2d2a52c86e2..2995b7e1602 100644 --- dighost.c +++ dighost.c @@ -935,10 +935,16 @@ parse_netprefix(struct sockaddr_storage **sap, int *plen, const char *value) { struct sockaddr_storage *sa = NULL; struct in_addr *in4; struct in6_addr *in6; - int prefix_length; + int prefix_length = -1, i; + char *sep; + char buf[INET6_ADDRSTRLEN + sizeof("/128")]; + const char *errstr; REQUIRE(sap != NULL && *sap == NULL); + if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) + fatal("invalid prefix '%s'\n", value); + sa = calloc(1, sizeof(*sa)); if (sa == NULL) fatal("out of memory"); @@ -952,14 +958,36 @@ parse_netprefix(struct sockaddr_storage **sap, int *plen, const char *value) { goto done; } - if ((prefix_length = inet_net_pton(AF_INET6, value, in6, sizeof(*in6))) - != -1) { + sep = strchr(buf, '/'); + if (sep != NULL) { + *sep++ = '\0'; + prefix_length = strtonum(sep, 0, 128, &errstr); + if (errstr != NULL) + fatal("invalid address '%s'", value); + } + + if (inet_pton(AF_INET6, buf, in6) == 1) { sa->ss_len = sizeof(struct sockaddr_in6); sa->ss_family = AF_INET6; - } else if ((prefix_length = inet_net_pton(AF_INET, value, in4, - sizeof(*in4))) != -1) { + if (prefix_length > 128 || prefix_length == -1) + prefix_length = 128; + } else if (inet_pton(AF_INET, buf, in4) == 1) { sa->ss_len = sizeof(struct sockaddr_in); sa->ss_family = AF_INET; + if (prefix_length > 32 || prefix_length == -1) + prefix_length = 32; + } else if (prefix_length != -1) { + if (prefix_length > 32) + prefix_length = 32; + for (i = 0; i < 3 ; i++) { + if (strlcat(buf, ".0", sizeof(buf)) > sizeof(buf)) + fatal("invalid address '%s'", value); + if (inet_pton(AF_INET, buf, in4) == 1) { + sa->ss_len = sizeof(struct sockaddr_in); + sa->ss_family = AF_INET; + goto done; + } + } } else fatal("invalid address '%s'", value); -- I'm not entirely sure you are real.