diff options
author | Eric S. Raymond <esr@thyrsus.com> | 2010-01-12 19:22:47 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2010-01-12 19:22:47 +0000 |
commit | da43ae4070286f4e8ab60e048893defe5cd48589 (patch) | |
tree | afb785cb9deedceebef934e1ef86124d98a3b43b /netlib.c | |
parent | 4d8ecc5b743a2d436daf9bb60232e09d5d02ab5c (diff) | |
download | gpsd-da43ae4070286f4e8ab60e048893defe5cd48589.tar.gz |
Internal IPv6 support, derived from a support patch by Olivier Mehani.
The patch original is at:
https://lists.berlios.de/pipermail/gpsd-dev/attachments/20100111/85ad4e15/attachment.bin
This revision changes netlib_connectsock() to take a first argument that is
an address family and can specify IPv4, IPv6, or either. It also changes
gpsd.c to open two client sockets, one IPv4 and one IPv6, and listen
on both.
As a required cleanup, a number of defaults to "127.0.0.1" become
defaults to "localhost" so we're not hardwiring in IPv4 assumptions
anymore.
I've omitted a significant portion of the Mehani patch that changed the
interface of the client library in an incompatible way. Currently there is
no way to make gpsd listen to IPv4 or IPv6 only, and no way to make a
client query over IPV4 or IPv6 only. Also, we'd really like to be able to
condition out IPv6 or (someday) IPv4 support for a leaner runtime, and
there's no way to do that yet, either.
Under IPv4, regression tests pass; live operation with a GPS mouse and
the aishub feed both work. However, the resulting code does not splint
clean; this will need to be fixed, and that's going to be tricky due
to the new sockaddr_t struct.
Diffstat (limited to 'netlib.c')
-rw-r--r-- | netlib.c | 96 |
1 files changed, 61 insertions, 35 deletions
@@ -38,29 +38,13 @@ #define INADDR_NONE ((in_addr_t)-1) #endif -socket_t netlib_connectsock(const char *host, const char *service, const char *protocol) +socket_t netlib_connectsock(int af, const char *host, const char *service, const char *protocol) { - struct hostent *phe; - struct servent *pse; struct protoent *ppe; - struct sockaddr_in sin; - int type, proto, one = 1; - socket_t s; - - memset((char *) &sin, 0, sizeof(sin)); - /*@ -type -mustfreefresh @*/ - sin.sin_family = AF_INET; - if ((pse = getservbyname(service, protocol))) - sin.sin_port = htons(ntohs((unsigned short) pse->s_port)); - else if ((sin.sin_port = htons((unsigned short) atoi(service))) == 0) - return NL_NOSERVICE; - - if ((phe = gethostbyname(host))) - memcpy((char *) &sin.sin_addr, phe->h_addr, phe->h_length); -#ifndef S_SPLINT_S - else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) - return NL_NOHOST; -#endif /* S_SPLINT_S */ + struct addrinfo hints; + struct addrinfo *result, *rp; + int ret, type, proto, one = 1; + socket_t s = -1; ppe = getprotobyname(protocol); if (strcmp(protocol, "udp") == 0) { @@ -71,16 +55,41 @@ socket_t netlib_connectsock(const char *host, const char *service, const char *p proto = (ppe) ? ppe->p_proto : IPPROTO_TCP; } - if ((s = socket(PF_INET, type, proto)) == -1) - return NL_NOSOCK; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))==-1) { - (void)close(s); - return NL_NOSOCKOPT; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = af; + hints.ai_socktype = type; + hints.ai_protocol = proto; + if((ret = getaddrinfo(host, service, &hints, &result))) { + return NL_NOHOST; } - if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) == -1) { - (void)close(s); - return NL_NOCONNECT; + + /* + * From getaddrinfo(3): + * Normally, the application should try using the addresses in the + * order in which they are returned. The sorting function used within + * getaddrinfo() is defined in RFC 3484). + * From RFC 3484 (Section 10.3): + * The default policy table gives IPv6 addresses higher precedence than + * IPv4 addresses. + * Thus, with the default palameters, we get IPv6 addresses first. + */ + for (rp = result; rp != NULL; rp = rp->ai_next) { + ret = NL_NOCONNECT; + if((s = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol)) < 0) + ret = NL_NOSOCK; + else if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))==-1) + ret = NL_NOSOCKOPT; + else if (!connect(s, rp->ai_addr, rp->ai_addrlen)) { + ret = 0; + break; + } + + if (s > 0) close(s); } + freeaddrinfo(result); + if (ret) + return ret; #ifdef IPTOS_LOWDELAY { @@ -113,18 +122,35 @@ char /*@observer@*/ *netlib_errstr(const int err) char *sock2ip(int fd) { - struct sockaddr fsin; + sockaddr_t fsin; socklen_t alen = (socklen_t)sizeof(fsin); - char *ip; + static char ip[INET6_ADDRSTRLEN]; int r; - r = getpeername(fd, (struct sockaddr *) &fsin, &alen); + r = getpeername(fd, &(fsin.sa), &alen); /*@ -branchstate @*/ if (r == 0) { - ip = inet_ntoa(((struct sockaddr_in *)(&fsin))->sin_addr); - } else { + switch (fsin.sa.sa_family) { + case AF_INET: + r = !inet_ntop(fsin.sa_in.sin_family, &(fsin.sa_in.sin_addr), + ip, sizeof(ip)); + break; + + case AF_INET6: + r = !inet_ntop(fsin.sa_in6.sin6_family, &(fsin.sa_in6.sin6_addr), + ip, sizeof(ip)); + break; + + default: + gpsd_report(LOG_ERROR, "Unhandled address family %d in %s\n", + fsin.sa.sa_family, __FUNCTION__); + strlcpy(ip,"<unknown AF>", sizeof(ip)); + return ip; + } + } + if (r != 0){ gpsd_report(LOG_INF, "getpeername() = %d, error = %s (%d)\n", r, strerror(errno), errno); - ip = "<unknown>"; + strlcpy(ip,"<unknown>", sizeof(ip)); } /*@ +branchstate @*/ return ip; |