diff options
author | Ian Jackson <ijackson@chiark.greenend.org.uk> | 2014-06-03 20:42:01 +0100 |
---|---|---|
committer | Ian Jackson <ijackson@chiark.greenend.org.uk> | 2014-10-19 21:08:41 +0100 |
commit | 5806b74528bbe1d70e5e1795224b1c6ecd8c4341 (patch) | |
tree | e9f5e5b1dd74a2995f4807f7e469851541a057cb /client | |
parent | 07554ccd8286775b4f88a608ba3b94ff7b9efaf9 (diff) |
New public-facing functions for address/text conversions.
The usual functions for doing these conversions are getaddrinfo(3) and
getnameinfo(3). Unfortunately, these seem generally to be rather buggy
(with different bugs on different platforms). For example, the Linux
glibc implementation tries to do complicated things with AF_NETLINK
sockets even though it's only going to do a simple syntactic
transformation.
So we provide our own versions, which only handle conversions between
addresses and their text numerical representations (and don't try to do
anything complicated with DNS).
For compatibility, the functions handle various crazy things which are
generally undesirable:
* traditional IPv4 text conversions allow degenerate forms A, A.B and
A.B.C, where the host part is given as a simple number rather than
being split into octets;
* traditional IPv4 text conversions allow the individual components to
be given in bases other than 10, using the usual C prefixes;
* IPv6 socket addresses (but, annoyingly, not `struct in6_addr') have
a `scope-id' field (which actually identifies what RFC4007 calls a
`zone', i.e., the namespace in which the address should be
interpreted), and these are described by a `%...' suffix; and
* the `scope-id' may be a name, though the syntax and meaning of such
names isn't defined anywhere except for the link-local scope, where
the names and numbers are interface names and indices, which need to
be looked up.
All of this means that there are a number of options and unfortunate
error conditions, which make the interface more complicated than is
really ideal.
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
Diffstat (limited to 'client')
-rw-r--r-- | client/Makefile.in | 2 | ||||
-rw-r--r-- | client/addrtext.c | 197 |
2 files changed, 198 insertions, 1 deletions
diff --git a/client/Makefile.in b/client/Makefile.in index 01f2fcd..0588072 100644 --- a/client/Makefile.in +++ b/client/Makefile.in @@ -27,7 +27,7 @@ PROGS_SYSDEP= @PROGS_HAVE_TSEARCH@ ENABLE_DYNAMIC= @ENABLE_DYNAMIC@ PROGRAMS= adnslogres adnsheloex adnshost $(PROGS_SYSDEP) -PROGRAMS_LOCAL= fanftest adnstest +PROGRAMS_LOCAL= fanftest adnstest addrtext PROGRAMS_ALL= $(PROGRAMS) $(PROGRAMS_LOCAL) STATIC_LIB= ../src/libadns.a diff --git a/client/addrtext.c b/client/addrtext.c new file mode 100644 index 0000000..f61aa68 --- /dev/null +++ b/client/addrtext.c @@ -0,0 +1,197 @@ +/* + some test cases + + + ./addrtext_s fe80::1%wlanx + ./addrtext_s fe80::1%wlan0 + ./addrtext_s fe80::1%23 + ./addrtext_s fe80::1%1 + ./addrtext_s 2001:ba8:1e3::%wlan0 + ./addrtext_s 2001:ba8:1e3::%23 + ./addrtext_s 2001:ba8:1e3::%1 # normally lo + ./addrtext_s 127.0.0.1x + ./addrtext_s 172.18.45.6 + ./addrtext_s 12345 + + + */ + +/* + * addrtext.c + * - test program for address<->string conversion, not part of the library + */ +/* + * This file is part of adns, which is + * Copyright (C) 1997-2000,2003,2006,2014 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology + * (See the file INSTALL for full details.) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <inttypes.h> + +#include "config.h" +#include "adns.h" + +#define PORT 1234 + +#define STRING(x) STRING2(x) +#define STRING2(x) #x + +static int fails; + +static void hex(const void *data_v, int l) { + const uint8_t *data= data_v; + int i; + for (i=0; i<l; i++) + printf("%02x",data[i]); +} + +static void dump(const char *pfx, struct sockaddr *sa, socklen_t salen) { + int i; + printf(" %s: ",pfx); + hex(sa, salen); + + for (i=0; i<salen; i++) + printf("%02x",((const uint8_t*)sa)[i]); + + printf(" %d ", sa->sa_family); + + switch (sa->sa_family) { + case AF_INET: { + const struct sockaddr_in *s = (const void*)sa; + printf(".port=%d .addr=%08"PRIx32"", + ntohs(s->sin_port), + (uint32_t)ntohl(s->sin_addr.s_addr)); + break; + } + case AF_INET6: { + const struct sockaddr_in6 *s = (const void*)sa; + printf(".port=%d .flowinfo=%08"PRIx32" .scope_id=%08"PRIx32" .addr=", + ntohs(s->sin6_port), + (uint32_t)ntohl(s->sin6_flowinfo), + (uint32_t)ntohl(s->sin6_scope_id)); + hex(&s->sin6_addr, sizeof(s->sin6_addr)); + break; + } + } + printf("\n"); +} + +static void dotest(const char *input) { + adns_sockaddr ours; + socklen_t socklen; + struct addrinfo aip; + struct addrinfo *air=0; + char ourbuf[ADNS_ADDR2TEXT_BUFLEN]; + char theirbuf[ADNS_ADDR2TEXT_BUFLEN]; + + memset(&ours,0,sizeof(ours)); + + socklen= sizeof(ours); + int our_r= adns_text2addr(input,PORT,0,&ours.sa,&socklen); + + memset(&aip,0,sizeof(aip)); + aip.ai_flags= AI_NUMERICHOST|AI_NUMERICSERV; + aip.ai_socktype= SOCK_DGRAM; + aip.ai_protocol= IPPROTO_UDP; + air= 0; + int libc_r= getaddrinfo(input,STRING(PORT),&aip,&air); + printf("`%s': us %s; libc %s, air=%p", + input, strerror(our_r), libc_r ? gai_strerror(libc_r) : "0", air); + if (air) + printf(" .family=%d .socklen=%ld .addr=%p .next=%p", + air->ai_family, (long)air->ai_addrlen, air->ai_addr, air->ai_next); + printf(":"); + + if (libc_r==EAI_NONAME && !air) { + if (strchr(input,'%') && (our_r==ENOSYS || our_r==ENXIO)) { + printf(" bad-scope"); + goto ok; + } + if (strchr(input,'%') && our_r==ENOSYS) { + printf(" bad-scope"); + goto ok; + } + if (our_r==EINVAL) { + printf(" invalid"); + goto ok; + } + } + printf(" valid"); + +#define FAIL do{ printf(" | FAIL\n"); fails++; }while(0) +#define WANT(x) if (!(x)) { printf(" not %s",STRING(x)); FAIL; return; } else; + + WANT(!our_r); + WANT(!libc_r); + WANT(air); + WANT(air->ai_addr); + WANT(!air->ai_next); + if (air->ai_addrlen!=socklen || memcmp(&ours,air->ai_addr,socklen)) { + printf(" mismatch"); + FAIL; + dump("ours",&ours.sa,socklen); + dump("libc",air->ai_addr,air->ai_addrlen); + return; + } + + printf(" |"); + + int ourbuflen= sizeof(ourbuf); + int ourport; + our_r= adns_addr2text(&ours.sa,0, ourbuf,&ourbuflen, &ourport); + + printf(" us %s",strerror(our_r)); + if (!our_r) + printf(" `%s'",ourbuf); + + size_t theirbuflen= sizeof(theirbuf); + libc_r= getnameinfo(&ours.sa,socklen, theirbuf,theirbuflen, 0,0, + NI_NUMERICHOST|NI_NUMERICSERV); + printf("; libc %s", libc_r ? gai_strerror(libc_r) : "0"); + if (!libc_r) + printf(" `%s'",theirbuf); + + printf(":"); + + WANT(!our_r); + WANT(!libc_r); + WANT(ourport==PORT); + if (strcmp(ourbuf,theirbuf)) { + printf(" mismatch"); + FAIL; + return; + } + + ok: + printf(" | PASS\n"); +} + +int main(int argc, char **argv) { + const char *arg; + while ((arg= *++argv)) { + dotest(arg); + } + return !!fails; +} |