git: 9front

Download patch

ref: c62c5941cabc959a6c1d85fd6b9928bcf7d70822
parent: 7472da59ea89ae5d8874f660960464a3bdbb12bc
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Sun Mar 31 14:52:45 EDT 2013

ape: initial IPv6 support, inet_pton()/inet_ntop(), getaddrinfo()/getnameinfo()

--- a/sys/include/ape/arpa/inet.h
+++ b/sys/include/ape/arpa/inet.h
@@ -1,146 +1,1 @@
-#ifndef __netinet_in__
-#define __netinet_in__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Copyright (c) 1982, 1986, 1990 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution is only permitted until one year after the first shipment
- * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
- * binary forms are permitted provided that: (1) source distributions retain
- * this entire copyright notice and comment, and (2) distributions including
- * binaries display the following acknowledgement:  This product includes
- * software developed by the University of California, Berkeley and its
- * contributors'' in the documentation or other materials provided with the
- * distribution and in all advertising materials mentioning features or use
- * of this software.  Neither the name of the University nor the names of
- * its contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- *	@(#)in.h	7.10 (Berkeley) 6/28/90 plus MULTICAST 1.1
- */
-
-/*
- * Constants and structures defined by the internet system,
- * Per RFC 790, September 1981.
- */
-
-/*
- * Protocols
- */
-#define	IPPROTO_IP		0		/* dummy for IP */
-#define	IPPROTO_ICMP		1		/* control message protocol */
-#define	IPPROTO_GGP		3		/* gateway^2 (deprecated) */
-#define	IPPROTO_TCP		6		/* tcp */
-#define	IPPROTO_EGP		8		/* exterior gateway protocol */
-#define	IPPROTO_PUP		12		/* pup */
-#define	IPPROTO_UDP		17		/* user datagram protocol */
-#define	IPPROTO_IDP		22		/* xns idp */
-#define	IPPROTO_TP		29 		/* tp-4 w/ class negotiation */
-#define	IPPROTO_EON		80		/* ISO cnlp */
-
-#define	IPPROTO_RAW		255		/* raw IP packet */
-#define	IPPROTO_MAX		256
-
-
-/*
- * Local port number conventions:
- * Ports < IPPORT_RESERVED are reserved for
- * privileged processes (e.g. root).
- * Ports > IPPORT_USERRESERVED are reserved
- * for servers, not necessarily privileged.
- */
-#define	IPPORT_RESERVED		1024
-#define	IPPORT_USERRESERVED	5000
-
-/*
- * Internet address (a structure for historical reasons)
- */
-struct in_addr {
-	unsigned long s_addr;
-};
-
-/*
- * Definitions of bits in internet address integers.
- * On subnets, the decomposition of addresses to host and net parts
- * is done according to subnet mask, not the masks here.
- */
-#define	IN_CLASSA(i)		(((long)(i) & 0x80000000) == 0)
-#define	IN_CLASSA_NET		0xff000000
-#define	IN_CLASSA_NSHIFT	24
-#define	IN_CLASSA_HOST		0x00ffffff
-#define	IN_CLASSA_MAX		128
-
-#define	IN_CLASSB(i)		(((long)(i) & 0xc0000000) == 0x80000000)
-#define	IN_CLASSB_NET		0xffff0000
-#define	IN_CLASSB_NSHIFT	16
-#define	IN_CLASSB_HOST		0x0000ffff
-#define	IN_CLASSB_MAX		65536
-
-#define	IN_CLASSC(i)		(((long)(i) & 0xe0000000) == 0xc0000000)
-#define	IN_CLASSC_NET		0xffffff00
-#define	IN_CLASSC_NSHIFT	8
-#define	IN_CLASSC_HOST		0x000000ff
-
-#define	IN_CLASSD(i)		(((long)(i) & 0xf0000000) == 0xe0000000)
-#define	IN_MULTICAST(i)		IN_CLASSD(i)
-
-#define	IN_EXPERIMENTAL(i)	(((long)(i) & 0xe0000000) == 0xe0000000)
-#define	IN_BADCLASS(i)		(((long)(i) & 0xf0000000) == 0xf0000000)
-
-#define	INADDR_ANY		(unsigned long)0x00000000
-#define	INADDR_BROADCAST	(unsigned long)0xffffffff	/* must be masked */
-
-#define	IN_LOOPBACKNET		127			/* official! */
-
-/*
- * Socket address, internet style.
- */
-struct sockaddr_in {
-	short	sin_family;
-	unsigned short	sin_port;
-	struct	in_addr sin_addr;
-	char	sin_zero[8];
-};
-
-/*
- * Structure used to describe IP options.
- * Used to store options internally, to pass them to a process,
- * or to restore options retrieved earlier.
- * The ip_dst is used for the first-hop gateway when using a source route
- * (this gets put into the header proper).
- */
-struct ip_opts {
-	struct	in_addr ip_dst;		/* first hop, 0 w/o src rt */
-	char	ip_opts[40];		/* actually variable in size */
-};
-
-/*
- * Options for use with [gs]etsockopt at the IP level.
- * First word of comment is data type; bool is stored in int.
- */
-#define	IP_OPTIONS	1	/* buf/ip_opts; set/get IP per-packet options */
-#define	IP_HDRINCL	7	/* int; header is included with data (raw) */
-#define	IP_TOS		8	/* int; IP type of service and precedence */
-#define	IP_TTL		9	/* int; IP time to live */
-
-extern unsigned long	ntohl(unsigned long x);
-extern unsigned short	ntohs(unsigned short x);
-extern unsigned long	htonl(unsigned long x);
-extern unsigned short	htons(unsigned short x);
-extern unsigned long	inet_addr(char*);
-extern char*		inet_ntoa(struct in_addr);
-extern unsigned long	nptohl(void*);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __netinet_in__ */
+#include <netinet/in.h>
--- a/sys/include/ape/netdb.h
+++ b/sys/include/ape/netdb.h
@@ -114,6 +114,54 @@
 
 #define __HOST_SVC_NOT_AVAIL 99		/* libc internal use only */
 
+struct	addrinfo {
+	int		ai_flags;	/* Input flags.  */
+	int		ai_family;	/* Protocol family for socket.  */
+	int		ai_socktype;	/* Socket type.  */
+	int		ai_protocol;	/* Protocol for socket.  */
+	int		ai_addrlen;	/* Length of socket address.  */
+	struct sockaddr	*ai_addr;	/* Socket address for socket.  */
+	char		*ai_canonname;	/* Canonical name for service location.  */
+	struct addrinfo	*ai_next;	/* Pointer to next in list.  */
+};
+
+extern int	getaddrinfo(char *, char *, struct addrinfo *, struct addrinfo **);
+extern void	freeaddrinfo(struct addrinfo *);
+extern int	getnameinfo(struct sockaddr *, int, char *, int, char *, int, unsigned int);
+extern char	*gai_strerror(int);
+
+/* Possible values for `ai_flags' field in `addrinfo' structure.  */
+#define AI_PASSIVE	0x0001	/* Socket address is intended for `bind'.  */
+#define AI_CANONNAME	0x0002	/* Request for canonical name.  */
+#define AI_NUMERICHOST	0x0004	/* Don't use name resolution.  */
+#define AI_V4MAPPED	0x0008	/* IPv4 mapped addresses are acceptable.  */
+#define AI_ALL		0x0010	/* Return IPv4 mapped and IPv6 addresses.  */
+#define AI_ADDRCONFIG	0x0020	/* Use configuration of this host to choose returned address type..  */
+#define AI_NUMERICSERV	0x0400	/* Don't use name resolution.  */
+
+/* getnameinfo flags */
+#define NI_NOFQDN	0x0001	/* Only the nodename portion of the FQDN is returned for local hosts. */
+#define NI_NUMERICHOST	0x0002	/* The numeric form of the node's address is returned instead of its name. */
+#define NI_NAMEREQD	0x0004	/* Return an error if the node's name cannot be located in the database. */
+#define NI_NUMERICSERV	0x0008	/* The numeric form of the service address is returned instead of its name. */
+#define NI_NUMERICSCOPE	0x0010	/* For IPv6 addresses, the numeric form of the scope identifier is returned
+				   instead of its name. */
+#define NI_DGRAM	0x0020	/* Indicates that the service is a datagram service (SOCK_DGRAM). */
+
+/* Error values for `getaddrinfo' and `getnameinfo' functions.  */
+#define EAI_BADFLAGS	  -1	/* Invalid value for `ai_flags' field */
+#define EAI_NONAME	  -2	/* NAME or SERVICE is unknown */
+#define EAI_AGAIN	  -3	/* Temporary failure in name resolution */
+#define EAI_FAIL	  -4	/* Non-recoverable failure in name resolution */
+#define EAI_NODATA	  -5	/* No address associated with NAME */
+#define EAI_FAMILY	  -6	/* `ai_family' not supported */
+#define EAI_SOCKTYPE	  -7	/* `ai_socktype' not supported */
+#define EAI_SERVICE	  -8	/* SERVICE not supported for `ai_socktype' */
+#define EAI_ADDRFAMILY	  -9	/* Address family for NAME not supported */
+#define EAI_MEMORY	  -10	/* Memory allocation failure */
+#define EAI_SYSTEM	  -11	/* System error returned in `errno' */
+#define EAI_OVERFLOW	  -12	/* Argument buffer overflow */
+
 #ifdef __cplusplus
 }
 #endif
--- a/sys/include/ape/netinet/in.h
+++ b/sys/include/ape/netinet/in.h
@@ -102,15 +102,41 @@
 #define	IN_LOOPBACKNET		127			/* official! */
 
 /*
+ * IPv6 internet address.
+ */ 
+struct in6_addr {
+	unsigned char s6_addr[16];
+};
+
+extern struct in6_addr in6addr_any;
+extern struct in6_addr in6addr_loopback;
+
+#define IN6ADDR_ANY_INIT \
+	{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}
+
+#define IN6ADDR_LOOPBACK_INIT \
+	{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}
+
+/*
  * Socket address, internet style.
  */
 struct sockaddr_in {
-	short	sin_family;
+	short		sin_family;
 	unsigned short	sin_port;
-	struct	in_addr sin_addr;
-	char	sin_zero[8];
+	struct in_addr	sin_addr;
+	char		sin_zero[8];
 };
 
+struct sockaddr_in6 {
+	short		sin6_family;
+	unsigned short	sin6_port;
+	unsigned long	sin6_flowinfo;
+	struct in6_addr	sin6_addr;
+	unsigned long	sin6_scope_id;
+};
+
 /*
  * Structure used to describe IP options.
  * Used to store options internally, to pass them to a process,
@@ -139,6 +165,12 @@
 extern unsigned long	inet_addr(char*);
 extern char*		inet_ntoa(struct in_addr);
 extern unsigned long	nptohl(void*);
+
+extern char*		inet_ntop(int af, void *src, char *dst, int size);
+extern int		inet_pton(int af, char *src, void *dst);
+
+#define INET_ADDRSTRLEN		16
+#define INET6_ADDRSTRLEN	46
 
 #ifdef __cplusplus
 }
--- a/sys/include/ape/sys/socket.h
+++ b/sys/include/ape/sys/socket.h
@@ -22,6 +22,8 @@
 /*
  * Definitions related to sockets: types, address families, options.
  */
+typedef int socklen_t;
+typedef unsigned short sa_family_t;
 
 /*
  * Types
@@ -108,7 +110,12 @@
  */
 struct sockaddr {
 	unsigned short	sa_family;	/* address family */
-	char	sa_data[108];
+	char		sa_data[108];
+};
+
+struct sockaddr_storage {
+	unsigned short	ss_family;
+	char		ss_data[108];
 };
 
 /*
--- a/sys/src/ape/lib/bsd/_sock_ingetaddr.c
+++ b/sys/src/ape/lib/bsd/_sock_ingetaddr.c
@@ -15,17 +15,74 @@
 
 #include "priv.h"
 
+void*
+_sock_inip(struct sockaddr *a)
+{
+	switch(a->sa_family){
+	case AF_INET:
+		return &((struct sockaddr_in*)a)->sin_addr;
+	case AF_INET6:
+		return &((struct sockaddr_in6*)a)->sin6_addr;
+	}
+	return 0;
+}
+
+int
+_sock_inport(struct sockaddr *a)
+{
+	switch(a->sa_family){
+	case AF_INET:
+		return ntohs(((struct sockaddr_in*)a)->sin_port);
+	case AF_INET6:
+		return ntohs(((struct sockaddr_in6*)a)->sin6_port);
+	}
+	return 0;
+}
+
+int
+_sock_inaddr(int af, char *ip, char *port, void *a, int *alen)
+{
+	int len;
+
+	len = 0;
+	if(af == AF_INET){
+		struct sockaddr_in *in = a;
+
+		len = sizeof(*in);
+		memset(in, 0, len);
+		in->sin_family = af;
+		if(port != 0 && *port != 0)
+			in->sin_port = htons(atoi(port));
+		if(ip != 0 && *ip != 0)
+			inet_pton(af, ip, &in->sin_addr);
+	} else if(af == AF_INET6){
+		struct sockaddr_in6 *in = a;
+
+		len = sizeof(*in);
+		memset(in, 0, len);
+		in->sin6_family = af;
+		if(port != 0 && *port != 0)
+			in->sin6_port = htons(atoi(port));
+		if(ip != 0 && *ip != 0)
+			inet_pton(af, ip, &in->sin6_addr);
+	}
+	if(alen != 0)
+		*alen = len;
+	return len;
+}
+
 void
-_sock_ingetaddr(Rock *r, struct sockaddr_in *ip, int *alen, char *a)
+_sock_ingetaddr(Rock *r, void *a, int *alen, char *file)
 {
+	char name[Ctlsize], *p;
 	int n, fd;
-	char *p;
-	char name[Ctlsize];
 
+	if(r->domain != PF_INET && r->domain != PF_INET6)
+		return;
 	/* get remote address */
 	strcpy(name, r->ctl);
 	p = strrchr(name, '/');
-	strcpy(p+1, a);
+	strcpy(p+1, file);
 	fd = open(name, O_RDONLY);
 	if(fd >= 0){
 		n = read(fd, name, sizeof(name)-1);
@@ -34,14 +91,9 @@
 			p = strchr(name, '!');
 			if(p){
 				*p++ = 0;
-				ip->sin_family = AF_INET;
-				ip->sin_port = htons(atoi(p));
-				ip->sin_addr.s_addr = inet_addr(name);
-				if(alen)
-					*alen = sizeof(struct sockaddr_in);
+				_sock_inaddr(r->domain, name, p, a, alen);
 			}
 		}
 		close(fd);
 	}
-
 }
--- a/sys/src/ape/lib/bsd/_sock_ipattr.c
+++ b/sys/src/ape/lib/bsd/_sock_ipattr.c
@@ -27,6 +27,8 @@
 			alpha = 1;
 		else if(*p == '.')
 			dot = 1;
+		else if(*p == ':')
+			return Tip;
 		else
 			return Tsys;
 	}
--- a/sys/src/ape/lib/bsd/_sock_srv.c
+++ b/sys/src/ape/lib/bsd/_sock_srv.c
@@ -44,12 +44,10 @@
 	sfd = creat(msg, 0666);
 	if(sfd < 0){
 		close(fd);
-		_syserrno();
 		return -1;
 	}
 	snprintf(msg, sizeof msg, "%d", fd);
 	if(write(sfd, msg, strlen(msg)) < 0){
-		_syserrno();
 		close(sfd);
 		close(fd);
 		return -1;
--- a/sys/src/ape/lib/bsd/accept.c
+++ b/sys/src/ape/lib/bsd/accept.c
@@ -20,7 +20,6 @@
 {
 	int n, nfd, cfd;
 	Rock *r, *nr;
-	struct sockaddr_in *ip;
 	char name[Ctlsize];
 	char file[8+Ctlsize+1];
 	char *p, *net;
@@ -33,6 +32,7 @@
 
 	switch(r->domain){
 	case PF_INET:
+	case PF_INET6:
 		switch(r->stype){
 		case SOCK_DGRAM:
 			net = "udp";
@@ -40,41 +40,35 @@
 		case SOCK_STREAM:
 			net = "tcp";
 			break;
+		case SOCK_RDM:
+			net = "il";
+			break;
 		}
 
 		/* get control file name from listener process */
 		n = read(fd, name, sizeof(name)-1);
-		if(n <= 0){
-			_syserrno();
+		if(n <= 0)
 			return -1;
-		}
 		name[n] = 0;
 		cfd = open(name, O_RDWR);
-		if(cfd < 0){
-			_syserrno();
+		if(cfd < 0)
 			return -1;
-		}
 
 		nfd = _sock_data(cfd, net, r->domain, r->stype, r->protocol, &nr);
-		if(nfd < 0){
-			_syserrno();
+		if(nfd < 0)
 			return -1;
-		}
 
 		if(write(fd, "OK", 2) < 0){
 			close(nfd);
-			_syserrno();
 			return -1;
 		}
-
 		/* get remote address */
-		ip = (struct sockaddr_in*)&nr->raddr;
-		_sock_ingetaddr(nr, ip, &n, "remote");
-		if(a){
-			memmove(a, ip, sizeof(struct sockaddr_in));
-			*alen = sizeof(struct sockaddr_in);
+		_sock_ingetaddr(nr, &nr->raddr, &n, "remote");
+		if(a != 0){
+			if(n > 0)
+				memmove(a, &nr->raddr, n);
+			*alen = n;
 		}
-
 		return nfd;
 	case PF_UNIX:
 		if(r->other >= 0){
--- a/sys/src/ape/lib/bsd/bind.c
+++ b/sys/src/ape/lib/bsd/bind.c
@@ -24,10 +24,9 @@
 int
 bind(int fd, void *a, int alen)
 {
-	int n, len, cfd;
+	int n, len, cfd, port;
 	Rock *r;
 	char msg[128];
-	struct sockaddr_in *lip;
 
 	/* assign the address */
 	r = _sock_findrock(fd, 0);
@@ -42,7 +41,7 @@
 	memmove(&r->addr, a, alen);
 
 	/* the rest is IP sepecific */
-	if (r->domain != PF_INET)
+	if (r->domain != PF_INET && r->domain != PF_INET6)
 		return 0;
 
 	cfd = open(r->ctl, O_RDWR);
@@ -50,9 +49,9 @@
 		errno = EBADF;
 		return -1;
 	}
-	lip = (struct sockaddr_in*)&r->addr;
-	if(lip->sin_port > 0)
-		snprintf(msg, sizeof msg, "bind %d", ntohs(lip->sin_port));
+	port = _sock_inport(&r->addr);
+	if(port > 0)
+		snprintf(msg, sizeof msg, "bind %d", port);
 	else
 		strcpy(msg, "bind *");
 	n = write(cfd, msg, strlen(msg));
@@ -62,9 +61,8 @@
 		return -1;
 	}
 	close(cfd);
-
-	if(lip->sin_port <= 0)
-		_sock_ingetaddr(r, lip, &len, "local");
+	if(port <= 0)
+		_sock_ingetaddr(r, &r->addr, 0, "local");
 
 	return 0;
 }
--- a/sys/src/ape/lib/bsd/connect.c
+++ b/sys/src/ape/lib/bsd/connect.c
@@ -21,7 +21,7 @@
 	Rock *r;
 	int n, cfd, nfd;
 	char msg[8+256+1], file[8+256+1];
-	struct sockaddr_in *lip, *rip;
+	struct sockaddr *sa;
 	struct sockaddr_un *runix;
 	static int vers;
 
@@ -34,35 +34,35 @@
 		errno = ENAMETOOLONG;
 		return -1;
 	}
+	sa = (struct sockaddr*)a;
+	if(sa->sa_family != r->domain){
+		errno = EAFNOSUPPORT;
+		return -1;
+	}
 	memmove(&r->raddr, a, alen);
 
 	switch(r->domain){
 	case PF_INET:
+	case PF_INET6:
 		/* set up a tcp or udp connection */
 		cfd = open(r->ctl, O_RDWR);
-		if(cfd < 0){
-			_syserrno();
+		if(cfd < 0)
 			return -1;
-		}
-		rip = a;
-		lip = (struct sockaddr_in*)&r->addr;
-		if(lip->sin_port)
+		if(_sock_inport(&r->addr) > 0) {
 			snprintf(msg, sizeof msg, "connect %s!%d%s %d",
-				inet_ntoa(rip->sin_addr), ntohs(rip->sin_port),
+				inet_ntop(sa->sa_family, _sock_inip(sa), file, sizeof(file)),
+				_sock_inport(sa),
 				r->reserved ? "!r" : "",
-				ntohs(lip->sin_port));
-		else
+				_sock_inport(&r->addr));
+		} else {
 			snprintf(msg, sizeof msg, "connect %s!%d%s",
-				inet_ntoa(rip->sin_addr), ntohs(rip->sin_port),
+				inet_ntop(sa->sa_family, _sock_inip(sa), file, sizeof(file)),
+				_sock_inport(sa),
 				r->reserved ? "!r" : "");
-		n = write(cfd, msg, strlen(msg));
-		if(n < 0){
-			_syserrno();
-			close(cfd);
-			return -1;
 		}
+		n = write(cfd, msg, strlen(msg));
 		close(cfd);
-		return 0;
+		return (n < 0) ? -1 : 0;
 	case PF_UNIX:
 		/* null terminate the address */
 		if(alen == sizeof(r->raddr))
@@ -87,12 +87,10 @@
 		_sock_srvname(file, runix->sun_path);
 		nfd = open(file, O_RDWR);
 		if(nfd < 0){
-			_syserrno();
 			unlink(msg);
 			return -1;
 		}
 		if(write(nfd, msg, strlen(msg)) < 0){
-			_syserrno();
 			close(nfd);
 			unlink(msg);
 			return -1;
--- /dev/null
+++ b/sys/src/ape/lib/bsd/gai_strerror.c
@@ -1,0 +1,34 @@
+/* posix */
+#include <sys/types.h>
+#include <unistd.h>
+
+/* bsd extensions */
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+char*
+gai_strerror(int err)
+{
+	static char *tab[] = {
+		/* 0 */			"No error",
+		/* EAI_BADFLAGS */	"Invalid value for `ai_flags' field",
+		/* EAI_NONAME */	"NAME or SERVICE is unknown",
+		/* EAI_AGAIN */		"Temporary failure in name resolution",
+		/* EAI_FAIL */		"Non-recoverable failure in name resolution",
+		/* EAI_NODATA */	"No address associated with NAME",
+		/* EAI_FAMILY */	"`ai_family' not supported",
+		/* EAI_SOCKTYPE */	"`ai_socktype' not supported",
+		/* EAI_SERVICE */	"SERVICE not supported for `ai_socktype'",
+		/* EAI_ADDRFAMILY */	"Address family for NAME not supported",
+		/* EAI_MEMORY */	"Memory allocation failure",
+		/* EAI_SYSTEM */	"System error returned in `errno'",
+		/* EAI_OVERFLOW */	"Argument buffer overflow",
+	};
+
+	err = -err;
+	if(err < 0 || err >= (sizeof(tab)/sizeof(tab[0])))
+		return "Unknown error";
+	return tab[err];
+}
--- /dev/null
+++ b/sys/src/ape/lib/bsd/getaddrinfo.c
@@ -1,0 +1,217 @@
+/* posix */
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+/* bsd extensions */
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "priv.h"
+
+/* for malloc/free */
+#include <stdlib.h>
+
+void
+freeaddrinfo(struct addrinfo *res)
+{
+	struct addrinfo *info;
+
+	while((info = res) != 0){
+		res = res->ai_next;
+		free(info->ai_canonname);
+		free(info->ai_addr);
+		free(info);
+	}
+}
+
+static int
+sockfamily(char *addr)
+{
+	if(strchr(addr, ':') != 0)
+		return AF_INET6;
+	else
+		return AF_INET;
+}
+
+static int
+sockproto(char *proto)
+{
+	if(strcmp(proto, "tcp") == 0)
+		return SOCK_STREAM;
+	if(strcmp(proto, "udp") == 0)
+		return SOCK_DGRAM;
+	if(strcmp(proto, "il") == 0)
+		return SOCK_RDM;
+	return 0;
+}
+
+static int
+filladdrinfo(char *s, struct addrinfo *a, struct addrinfo *h)
+{
+	struct sockaddr sa;
+	char *p, *q;
+
+	if(*s != '/')
+		return 1;
+	if((q = strchr(s, ' ')) == 0)
+		return 1;
+	while(*q == ' ')
+		*q++ = 0;
+	if((p = strrchr(s+1, '/')) == 0)
+		return 1;
+	*p = 0;
+	if((p = strrchr(s+1, '/')) == 0)
+		return 1;
+	*p++ = 0;
+	if(*p == 0)
+		return 1;
+
+	if((a->ai_socktype = sockproto(p)) == 0)
+		return 1;
+	if((p = strchr(q, '!')) != 0){
+		*p++ = 0;
+		a->ai_family = sockfamily(q);
+	} else{
+		p = q;
+		q = 0;
+		if((a->ai_family = h->ai_family) == 0)
+			a->ai_family = AF_INET;
+	}
+	if((a->ai_socktype == SOCK_RDM || h->ai_socktype != 0)
+	&& (a->ai_socktype != h->ai_socktype))
+		return 1;
+	if(h->ai_family != 0 && a->ai_family != h->ai_family)
+		return 1;
+	if(_sock_inaddr(a->ai_family, q, p, &sa, &a->ai_addrlen) <= 0)
+		return 1;
+	if((a->ai_addr = malloc(a->ai_addrlen)) == 0)
+		return EAI_MEMORY;
+	memmove(a->ai_addr, &sa, a->ai_addrlen);
+	return 0;
+}
+
+int
+getaddrinfo(char *node, char *serv, struct addrinfo *hints, struct addrinfo **res)
+{
+	static struct addrinfo nohints;
+	struct addrinfo *a, *head, **tail;
+	char buf[1024], *proto;
+	int n, fd, err;
+
+	if(res != 0)
+		*res = 0;
+
+	if(hints == 0)
+		hints = &nohints;
+
+	proto = "net";
+	switch(hints->ai_family){
+	default:
+		return EAI_FAMILY;
+	case AF_INET:
+	case AF_INET6:
+		switch(hints->ai_socktype){
+		default:
+			return EAI_SOCKTYPE;
+		case SOCK_STREAM:
+			proto = "tcp";
+			break;
+		case SOCK_DGRAM:
+			proto = "udp";
+			break;
+		case SOCK_RDM:
+			proto = "il";
+			break;
+		case 0:
+			break;
+		}
+		break;
+	case AF_UNSPEC:
+		break;
+	}
+	if(serv == 0){
+		if(node == 0)
+			return EAI_NONAME;
+		serv = "0";
+	}
+	if(node == 0){
+		if(hints->ai_flags & AI_PASSIVE)
+			node = "*";
+		else if(hints->ai_family == AF_INET6)
+			node = "::1";
+		else
+			node = "127.0.0.1";
+	}
+
+	if((fd = open("/net/cs", O_RDWR)) < 0)
+		return EAI_SYSTEM;
+
+	snprintf(buf, sizeof(buf), "%s!%s!%s", proto, node, serv);
+	n = strlen(buf);
+	if(write(fd, buf, n) != n){
+		close(fd);
+		return EAI_AGAIN;
+	}
+	lseek(fd, 0, 0);
+
+	head = 0;
+	tail = &head;
+	for(;;){
+		if((n = read(fd, buf, sizeof(buf)-1)) <= 0)
+			break;
+		buf[n] = '\0';
+		if((a = malloc(sizeof(*a))) == 0){
+			freeaddrinfo(head);
+			close(fd);
+			return EAI_MEMORY;
+		}
+		memset(a, 0, sizeof(*a));
+		if((err = filladdrinfo(buf, a, hints)) != 0){
+			freeaddrinfo(a);
+			if(err < 0){
+				freeaddrinfo(head);
+				close(fd);
+				return err;
+			}
+		} else {
+			*tail = a;
+			tail = &a->ai_next;
+		}
+	}
+	close(fd);
+
+	if(head == 0)
+		return EAI_NODATA;
+
+	if((hints->ai_flags & AI_CANONNAME) != 0 && (hints->ai_flags & AI_NUMERICHOST) == 0){
+		n = _sock_ipattr(node);
+		if(n != Tsys && n != Tdom){
+			if(getnameinfo(head->ai_addr, head->ai_addrlen, buf, sizeof(buf), 0, 0, NI_NAMEREQD) == 0)
+				node = buf;
+			else
+				node = 0;
+		}
+		if(node != 0){
+			n = strlen(node)+1;
+			if((head->ai_canonname = malloc(n)) == 0){
+				freeaddrinfo(head);
+				return EAI_MEMORY;
+			}
+			memmove(head->ai_canonname, node, n);
+		}
+	}
+
+	if(res != 0)
+		*res = head;
+	else
+		freeaddrinfo(head);
+
+	return 0;
+}
--- a/sys/src/ape/lib/bsd/gethostbyname.c
+++ b/sys/src/ape/lib/bsd/gethostbyname.c
@@ -29,7 +29,7 @@
 gethostbyname(char *name)
 {
 	int i, t, fd, m;
-	char *p, *bp;
+	char *p, *k, *bp;
 	int nn, na;
 	unsigned long x;
 	static struct hostent h;
@@ -44,7 +44,6 @@
 	/* connect to server */
 	fd = open("/net/cs", O_RDWR);
 	if(fd < 0){
-		_syserrno();
 		h_errno = NO_RECOVERY;
 		return 0;
 	}
@@ -64,8 +63,8 @@
 
 	/* query the server */
 	if(write(fd, buf, strlen(buf)) < 0){
-		_syserrno();
 		h_errno = TRY_AGAIN;
+		close(fd);
 		return 0;
 	}
 	lseek(fd, 0, 0);
@@ -81,19 +80,26 @@
 	/* parse the reply */
 	nn = na = 0;
 	for(bp = buf;;){
-		p = strchr(bp, '=');
+		k = bp;
+		p = strchr(k, '=');
 		if(p == 0)
 			break;
 		*p++ = 0;
-		if(strcmp(bp, "dom") == 0){
+		for(bp = p; *bp && *bp != ' '; bp++)
+			;
+		if(*bp)
+			*bp++ = 0;
+		if(strcmp(k, "dom") == 0){
 			if(h.h_name == 0)
 				h.h_name = p;
 			if(nn < Nname)
 				nptr[nn++] = p;
-		} else if(strcmp(bp, "sys") == 0){
+		} else if(strcmp(k, "sys") == 0){
 			if(nn < Nname)
 				nptr[nn++] = p;
-		} else if(strcmp(bp, "ip") == 0){
+		} else if(strcmp(k, "ip") == 0){
+			if(strchr(p, ':') != 0)
+				continue;	/* ignore ipv6 addresses */
 			x = inet_addr(p);
 			x = ntohl(x);
 			if(na < Nname){
@@ -105,11 +111,6 @@
 				na++;
 			}
 		}
-		while(*p && *p != ' ')
-			p++;
-		if(*p)
-			*p++ = 0;
-		bp = p;
 	}
 	if(nn+na == 0){
 		h_errno = HOST_NOT_FOUND;
--- /dev/null
+++ b/sys/src/ape/lib/bsd/getnameinfo.c
@@ -1,0 +1,126 @@
+/* posix */
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+/* bsd extensions */
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "priv.h"
+
+static int
+netquery(char *buf, int nbuf)
+{
+	int fd, i, n;
+
+	if((fd = open("/net/cs", O_RDWR)) < 0)
+		return EAI_SYSTEM;
+	n = strlen(buf);
+	if(write(fd, buf, n) != n){
+		close(fd);
+		return EAI_NONAME;
+	}
+	lseek(fd, 0, 0);
+	for(i = 0; i < nbuf-1; i += n){
+		n = read(fd, buf+i, nbuf - 1 - i);
+		if(n <= 0)
+			break;
+		buf[i+n++] = ' ';
+	}
+	close(fd);
+	buf[i] = 0;
+	return i;
+}
+
+int
+getnameinfo(struct sockaddr *sa, int salen,
+	char *host, int hostlen,
+	char *serv, int servlen,
+	unsigned int flags)
+{
+	char buf[8*1024], *b, *p;
+	int err;
+
+	if(sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
+		return EAI_FAMILY;
+
+	if(host != 0 && hostlen > 0){
+		if(inet_ntop(sa->sa_family, _sock_inip(sa), host, hostlen) == 0)
+			return EAI_SYSTEM;
+
+		if((flags & NI_NUMERICHOST) == 0){
+			snprintf(buf, sizeof(buf), "!ip=%s", host);
+			if((err = netquery(buf, sizeof(buf))) < 0){
+				if((flags & NI_NAMEREQD) != 0)
+					return err;
+			} else {
+				char *sys, *dom;
+
+				sys = dom = 0;
+				for(b = buf;;){
+					if((p = strchr(b, '=')) == 0)
+						break;
+					*p++ = 0;
+					if(strcmp(b, "sys") == 0)
+						sys = p;
+					else if(strcmp(b, "dom") == 0)
+						dom = p;
+					while(*p && *p != ' ')
+						p++;
+					while(*p == ' ')
+						*p++ = 0;
+					b = p;
+				}
+				if(sys == 0){
+					if(dom == 0 && (flags & NI_NAMEREQD) != 0)
+						return EAI_NONAME;
+					if(dom != 0 && (flags & NI_NOFQDN) != 0){
+						if((p = strchr(dom, '.')) != 0)
+							*p = 0;
+					}
+					sys = dom;
+				}
+				snprintf(host, hostlen, "%s", sys);
+			}
+		}
+	}
+
+	if(serv != 0 && servlen > 0){
+		snprintf(serv, servlen, "%d", _sock_inport(sa));
+		if((flags & NI_NUMERICSERV) == 0){
+			snprintf(buf, sizeof(buf), "!port=%s", serv);
+			if(netquery(buf, sizeof(buf)) > 0){
+				char *tcp, *udp;
+
+				tcp = udp = 0;
+				for(b = buf;;){
+					if((p = strchr(b, '=')) == 0)
+						break;
+					*p++ = 0;
+					if(strcmp(b, "tcp") == 0)
+						tcp = p;
+					else if(strcmp(b, "udp") == 0)
+						udp = p;
+					while(*p && *p != ' ')
+						p++;
+					while(*p == ' ')
+						*p++ = 0;
+					b = p;
+				}
+				if(udp != 0 && (flags & NI_DGRAM) != 0)
+					snprintf(serv, servlen, "%s", udp);
+				else if(tcp != 0)
+					snprintf(serv, servlen, "%s", tcp);
+			}
+		}
+	}
+
+	return 0;
+}
--- a/sys/src/ape/lib/bsd/getpeername.c
+++ b/sys/src/ape/lib/bsd/getpeername.c
@@ -20,7 +20,6 @@
 {
 	Rock *r;
 	int i;
-	struct sockaddr_in *rip;
 	struct sockaddr_un *runix;
 
 	r = _sock_findrock(fd, 0);
@@ -31,9 +30,12 @@
 
 	switch(r->domain){
 	case PF_INET:
-		rip = (struct sockaddr_in*)&r->raddr;
-		memmove(addr, rip, sizeof(struct sockaddr_in));
+		memmove(addr, &r->raddr, sizeof(struct sockaddr_in));
 		*alen = sizeof(struct sockaddr_in);
+		break;
+	case PF_INET6:
+		memmove(addr, &r->raddr, sizeof(struct sockaddr_in6));
+		*alen = sizeof(struct sockaddr_in6);
 		break;
 	case PF_UNIX:
 		runix = (struct sockaddr_un*)&r->raddr;
--- a/sys/src/ape/lib/bsd/getprotobyname.c
+++ b/sys/src/ape/lib/bsd/getprotobyname.c
@@ -35,7 +35,6 @@
 	/* connect to server */
 	fd = open("/net/cs", O_RDWR);
 	if(fd < 0){
-		_syserrno();
 		h_errno = NO_RECOVERY;
 		return 0;
 	}
@@ -45,8 +44,8 @@
 
 	/* query the server */
 	if(write(fd, buf, strlen(buf)) < 0){
-		_syserrno();
 		h_errno = TRY_AGAIN;
+		close(fd);
 		return 0;
 	}
 	lseek(fd, 0, 0);
--- a/sys/src/ape/lib/bsd/getservbyname.c
+++ b/sys/src/ape/lib/bsd/getservbyname.c
@@ -43,10 +43,8 @@
 
 	/* connect to server */
 	fd = open("/net/cs", O_RDWR);
-	if(fd < 0){
-		_syserrno();
+	if(fd < 0)
 		return 0;
-	}
 
 	/* construct the query, always expect an ip# back */
 	if(num)
@@ -56,7 +54,7 @@
 
 	/* query the server */
 	if(write(fd, buf, strlen(buf)) < 0){
-		_syserrno();
+		close(fd);
 		return 0;
 	}
 	lseek(fd, 0, 0);
--- a/sys/src/ape/lib/bsd/getsockname.c
+++ b/sys/src/ape/lib/bsd/getsockname.c
@@ -20,7 +20,6 @@
 {
 	Rock *r;
 	int i;
-	struct sockaddr_in *lip;
 	struct sockaddr_un *lunix;
 
 	r = _sock_findrock(fd, 0);
@@ -31,14 +30,15 @@
 
 	switch(r->domain){
 	case PF_INET:
-		lip = (struct sockaddr_in*)addr;
-		_sock_ingetaddr(r, lip, alen, "local");
+	case PF_INET6:
+		_sock_ingetaddr(r, addr, alen, "local");
 		break;
 	case PF_UNIX:
 		lunix = (struct sockaddr_un*)&r->addr;
 		i = &lunix->sun_path[strlen(lunix->sun_path)] - (char*)lunix;
 		memmove(addr, lunix, i);
-		*alen = i;
+		if(alen != 0)
+			*alen = i;
 		break;
 	default:
 		errno = EAFNOSUPPORT;
--- /dev/null
+++ b/sys/src/ape/lib/bsd/in6_addr.c
@@ -1,0 +1,4 @@
+#include <netinet/in.h>
+
+struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
--- /dev/null
+++ b/sys/src/ape/lib/bsd/inet_ntop.c
@@ -1,0 +1,54 @@
+/* posix */
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* bsd extensions */
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+
+char*
+inet_ntop(int af, void *src, char *dst, int size)
+{
+	unsigned char *p;
+	char *t, *e;
+	int i;
+
+	if(af == AF_INET){
+		if(size < INET_ADDRSTRLEN){
+			errno = ENOSPC;
+			return 0;
+		}
+		p = (unsigned char*)&(((struct in_addr*)src)->s_addr);
+		snprintf(dst, size, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+		return dst;
+	}
+
+	if(af != AF_INET6){
+		errno = EAFNOSUPPORT;
+		return 0;
+	}
+	if(size < INET6_ADDRSTRLEN){
+		errno = ENOSPC;
+		return 0;
+	}
+
+	p = (unsigned char*)((struct in6_addr*)src)->s6_addr;
+	t = dst;
+	e = t + size;
+	for(i=0; i<16; i += 2){
+		unsigned int w;
+
+		if(i > 0)
+			*t++ = ':';
+		w = p[i]<<8 | p[i+1];
+		snprintf(t, e - t, "%x", w);
+		t += strlen(t);
+	}
+	return dst;
+}
--- /dev/null
+++ b/sys/src/ape/lib/bsd/inet_pton.c
@@ -1,0 +1,79 @@
+/* posix */
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/* bsd extensions */
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+
+static int
+ipcharok(int c)
+{
+	return c == ':' || isascii(c) && isxdigit(c);
+}
+
+static int
+delimchar(int c)
+{
+	if(c == '\0')
+		return 1;
+	if(c == ':' || isascii(c) && isalnum(c))
+		return 0;
+	return 1;
+}
+
+int
+inet_pton(int af, char *src, void *dst)
+{
+	int i, elipsis = 0;
+	unsigned char *to;
+	unsigned long x;
+	char *p, *op;
+
+	if(af == AF_INET){
+		((struct in_addr*)dst)->s_addr = inet_addr(src);
+		return 1;
+	}
+
+	if(af != AF_INET6){
+		errno = EAFNOSUPPORT;
+		return -1;
+	}
+
+	to = ((struct in6_addr*)dst)->s6_addr;
+	memset(to, 0, 16);
+
+	p = src;
+	for(i = 0; i < 16 && ipcharok(*p); i+=2){
+		op = p;
+		x = strtoul(p, &p, 16);
+
+		if(x != (unsigned short)x || *p != ':' && !delimchar(*p))
+			return 0;			/* parse error */
+
+		to[i] = x>>8;
+		to[i+1] = x;
+		if(*p == ':'){
+			if(*++p == ':'){	/* :: is elided zero short(s) */
+				if (elipsis)
+					return 0;	/* second :: */
+				elipsis = i+2;
+				p++;
+			}
+		} else if (p == op)		/* strtoul made no progress? */
+			break;
+	}
+	if (p == src || !delimchar(*p))
+		return 0;				/* parse error */
+	if(i < 16){
+		memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
+		memset(&to[elipsis], 0, 16-i);
+	}
+	return 1;
+}
--- a/sys/src/ape/lib/bsd/listen.c
+++ b/sys/src/ape/lib/bsd/listen.c
@@ -47,6 +47,9 @@
 	case SOCK_STREAM:
 		net = "tcp";
 		break;
+	case SOCK_RDM:
+		net = "il";
+		break;
 	}
 
 	strcpy(listen, r->ctl);
@@ -118,9 +121,8 @@
 	int backlog;
 {
 	Rock *r;
-	int n, cfd;
+	int n, cfd, port;
 	char msg[128];
-	struct sockaddr_in *lip;
 	struct sockaddr_un *lunix;
 
 	r = _sock_findrock(fd, 0);
@@ -131,20 +133,20 @@
 
 	switch(r->domain){
 	case PF_INET:
+	case PF_INET6:
 		cfd = open(r->ctl, O_RDWR);
 		if(cfd < 0){
 			errno = EBADF;
 			return -1;
 		}
-		lip = (struct sockaddr_in*)&r->addr;
-		if(lip->sin_port >= 0) {
+		port = _sock_inport(&r->addr);
+		if(port >= 0) {
 			if(write(cfd, "bind 0", 6) < 0) {
 				errno = EGREG;
 				close(cfd);
 				return -1;
 			}
-			snprintf(msg, sizeof msg, "announce %d",
-				ntohs(lip->sin_port));
+			snprintf(msg, sizeof msg, "announce %d", port);
 		}
 		else
 			strcpy(msg, "announce *");
@@ -164,7 +166,6 @@
 		}
 		lunix = (struct sockaddr_un*)&r->addr;
 		if(_sock_srv(lunix->sun_path, r->other) < 0){
-			_syserrno();
 			r->other = -1;
 			return -1;
 		}
--- a/sys/src/ape/lib/bsd/mkfile
+++ b/sys/src/ape/lib/bsd/mkfile
@@ -9,10 +9,13 @@
 	connect.$O\
 	endhostent.$O\
 	ffs.$O\
+	gai_strerror.$O\
+	getaddrinfo.$O\
 	getdtablesize.$O\
-	gethostbyname.$O\
 	gethostbyaddr.$O\
+	gethostbyname.$O\
 	gethostname.$O\
+	getnameinfo.$O\
 	getopt.$O\
 	getpeername.$O\
 	getprotobyname.$O\
@@ -20,8 +23,11 @@
 	getservbyname.$O\
 	getsockname.$O\
 	gettimeofday.$O\
+	in6_addr.$O\
 	inet_addr.$O\
 	inet_ntoa.$O\
+	inet_ntop.$O\
+	inet_pton.$O\
 	ioctl.$O\
 	listen.$O\
 	lstat.$O\
--- a/sys/src/ape/lib/bsd/priv.h
+++ b/sys/src/ape/lib/bsd/priv.h
@@ -41,6 +41,7 @@
 extern int	_sock_srv(char*, int);
 extern int	_sock_data(int, char*, int, int, int, Rock**);
 extern int	_sock_ipattr(char*);
-extern void	_sock_ingetaddr(Rock*, struct sockaddr_in*, int*, char*);
-
-extern void	_syserrno(void);
+extern void*	_sock_inip(struct sockaddr*);
+extern int	_sock_inport(struct sockaddr*);
+extern int	_sock_inaddr(int, char*, char*, void*, int*);
+extern void	_sock_ingetaddr(Rock*, void*, int*, char*);
--- a/sys/src/ape/lib/bsd/socket.c
+++ b/sys/src/ape/lib/bsd/socket.c
@@ -125,6 +125,7 @@
 
 	switch(domain){
 	case PF_INET:
+	case PF_INET6:
 		/* get a free network directory */
 		switch(stype){
 		case SOCK_DGRAM:
@@ -135,20 +136,20 @@
 			net = "tcp";
 			cfd = open("/net/tcp/clone", O_RDWR);
 			break;
+		case SOCK_RDM:
+			net = "il";
+			cfd = open("/net/il/clone", O_RDWR);
+			break;
 		default:
 			errno = EPROTONOSUPPORT;
 			return -1;
 		}
-		if(cfd < 0){
-			_syserrno();
+		if(cfd < 0)
 			return -1;
-		}
 		return _sock_data(cfd, net, domain, stype, protocol, 0);
 	case PF_UNIX:
-		if(pipe(pfd) < 0){
-			_syserrno();
+		if(pipe(pfd) < 0)
 			return -1;
-		}
 		r = _sock_newrock(pfd[0]);
 		if(r == 0){
 			close(pfd[0]);
--- a/sys/src/ape/lib/bsd/writev.c
+++ b/sys/src/ape/lib/bsd/writev.c
@@ -33,12 +33,10 @@
 			f += i;
 			i = write(fd, buf, sizeof(buf));
 			if(i < 0){
-				if(written > 0){
+				if(written > 0)
 					return written;
-				}else{
-					_syserrno();
+				else
 					return -1;
-				}
 			}
 			written += i;
 			if(i != sizeof(buf)) {
@@ -51,10 +49,8 @@
 	if(i > 0){
 		n = write(fd, buf, i);
 		if(n < 0){
-			if(written == 0){
-				_syserrno();
+			if(written == 0)
 				return -1;
-			}
 		} else
 			written += n;
 	}
--