summaryrefslogtreecommitdiff
path: root/netlib.c
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2010-01-12 19:22:47 +0000
committerEric S. Raymond <esr@thyrsus.com>2010-01-12 19:22:47 +0000
commitda43ae4070286f4e8ab60e048893defe5cd48589 (patch)
treeafb785cb9deedceebef934e1ef86124d98a3b43b /netlib.c
parent4d8ecc5b743a2d436daf9bb60232e09d5d02ab5c (diff)
downloadgpsd-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.c96
1 files changed, 61 insertions, 35 deletions
diff --git a/netlib.c b/netlib.c
index 10d4df5b..fb83fc37 100644
--- a/netlib.c
+++ b/netlib.c
@@ -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;