about summary refs log tree commit diff
path: root/src/network
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-02-12 00:22:29 -0500
committerRich Felker <dalias@aerifal.cx>2011-02-12 00:22:29 -0500
commit0b44a0315b47dd8eced9f3b7f31580cf14bbfc01 (patch)
tree6eaef0d8a720fa3da580de87b647fff796fe80b3 /src/network
downloadmusl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.tar.gz
musl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.tar.xz
musl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.zip
initial check-in, version 0.5.0 v0.5.0
Diffstat (limited to 'src/network')
-rw-r--r--src/network/__dns.c267
-rw-r--r--src/network/__dns.h14
-rw-r--r--src/network/__ipparse.c40
-rw-r--r--src/network/accept.c14
-rw-r--r--src/network/bind.c9
-rw-r--r--src/network/connect.c14
-rw-r--r--src/network/dn_expand.c28
-rw-r--r--src/network/ent.c26
-rw-r--r--src/network/freeaddrinfo.c7
-rw-r--r--src/network/gai_strerror.c21
-rw-r--r--src/network/getaddrinfo.c224
-rw-r--r--src/network/gethostbyaddr.c15
-rw-r--r--src/network/gethostbyaddr_r.c71
-rw-r--r--src/network/gethostbyname.c63
-rw-r--r--src/network/gethostbyname2.c16
-rw-r--r--src/network/gethostbyname2_r.c99
-rw-r--r--src/network/gethostbyname_r.c11
-rw-r--r--src/network/getnameinfo.c54
-rw-r--r--src/network/getpeername.c9
-rw-r--r--src/network/getservbyname.c12
-rw-r--r--src/network/getservbyname_r.c41
-rw-r--r--src/network/getservbyport.c12
-rw-r--r--src/network/getservbyport_r.c43
-rw-r--r--src/network/getsockname.c9
-rw-r--r--src/network/getsockopt.c13
-rw-r--r--src/network/h_errno.c1
-rw-r--r--src/network/hstrerror.c16
-rw-r--r--src/network/htonl.c10
-rw-r--r--src/network/htons.c10
-rw-r--r--src/network/in6addr_any.c3
-rw-r--r--src/network/in6addr_loopback.c3
-rw-r--r--src/network/inet_addr.c11
-rw-r--r--src/network/inet_aton.c7
-rw-r--r--src/network/inet_ntoa.c10
-rw-r--r--src/network/inet_ntop.c48
-rw-r--r--src/network/inet_pton.c31
-rw-r--r--src/network/listen.c9
-rw-r--r--src/network/ntohl.c10
-rw-r--r--src/network/ntohs.c10
-rw-r--r--src/network/proto.c58
-rw-r--r--src/network/recv.c14
-rw-r--r--src/network/recvfrom.c17
-rw-r--r--src/network/recvmsg.c14
-rw-r--r--src/network/res_init.c4
-rw-r--r--src/network/res_query.c20
-rw-r--r--src/network/send.c14
-rw-r--r--src/network/sendmsg.c14
-rw-r--r--src/network/sendto.c17
-rw-r--r--src/network/serv.c16
-rw-r--r--src/network/setsockopt.c9
-rw-r--r--src/network/shutdown.c9
-rw-r--r--src/network/sockatmark.c11
-rw-r--r--src/network/socket.c9
-rw-r--r--src/network/socketcall.h24
-rw-r--r--src/network/socketpair.c9
55 files changed, 1570 insertions, 0 deletions
diff --git a/src/network/__dns.c b/src/network/__dns.c
new file mode 100644
index 00000000..73ec422d
--- /dev/null
+++ b/src/network/__dns.c
@@ -0,0 +1,267 @@
+#include <stdint.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <time.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "__dns.h"
+#include "stdio_impl.h"
+
+#define TIMEOUT 5
+#define RETRY 1
+#define PACKET_MAX 512
+#define PTR_MAX (64 + sizeof ".in-addr.arpa")
+
+int __dns_doqueries(unsigned char *dest, const char *name, int *rr, int rrcnt)
+{
+	time_t t0 = time(0);
+	int fd;
+	FILE *f, _f;
+	unsigned char _buf[64];
+	char line[64], *s, *z;
+	union {
+		struct sockaddr_in sin;
+		struct sockaddr_in6 sin6;
+	} sa = {0}, ns[3] = {{0}};
+	socklen_t sl;
+	int nns;
+	int family;
+	unsigned char q[280] = "", *r = dest;
+	int ql;
+	int rlen;
+	int got = 0, failed = 0;
+	int errcode = EAI_AGAIN;
+	int i, j;
+	struct timeval tv;
+	fd_set fds;
+	int id;
+
+	/* Construct query template - RR and ID will be filled later */
+	if (strlen(name)-1 >= 254U) return -1;
+	q[2] = q[5] = 1;
+	strcpy(q+13, name);
+	for (i=13; q[i]; i=j+1) {
+		for (j=i; q[j] && q[j] != '.'; j++);
+		if (j-i-1u > 62u) return -1;
+		q[i-1] = j-i;
+	}
+	q[i+3] = 1;
+	ql = i+4;
+
+	/* Make a reasonably unpredictable id */
+	gettimeofday(&tv, 0);
+	id = tv.tv_usec + tv.tv_usec/256 & 0xffff;
+
+	/* Get nameservers from resolv.conf, fallback to localhost */
+	f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf);
+	if (f) for (nns=0; nns<3 && fgets(line, sizeof line, f); ) {
+		if (strncmp(line, "nameserver", 10) || !isspace(line[10]))
+			continue;
+		for (s=line+11; isspace(*s); s++);
+		for (z=s; *z && !isspace(*z); z++);
+		*z=0;
+		if (__ipparse(ns+nns, family, s) < 0) continue;
+		ns[nns].sin.sin_port = htons(53);
+		family = ns[nns++].sin.sin_family;
+		sl = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
+	}
+	if (f) __fclose_ca(f);
+	if (!nns) {
+		ns[0].sin.sin_family = AF_INET;
+		ns[0].sin.sin_port = htons(53);
+		nns=1;
+		sl = sizeof sa.sin;
+	}
+
+	/* Get local address and open/bind a socket */
+	sa.sin.sin_family = family;
+	fd = socket(family, SOCK_DGRAM, 0);
+	if (bind(fd, (void *)&sa, sl) < 0) {
+		close(fd);
+		return -1;
+	}
+	/* Nonblocking to work around Linux UDP select bug */
+	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+
+	/* Loop until we timeout; break early on success */
+	for (; time(0)-t0 < TIMEOUT; ) {
+
+		/* Query all configured namservers in parallel */
+		for (i=0; i<rrcnt; i++) if (rr[i]) for (j=0; j<nns; j++) {
+			q[0] = id+i >> 8;
+			q[1] = id+i;
+			q[ql-3] = rr[i];
+			sendto(fd, q, ql, MSG_NOSIGNAL, (void *)&ns[j], sl);
+		}
+
+		/* Wait for a response, or until time to retry */
+		FD_ZERO(&fds);
+		FD_SET(fd, &fds);
+		tv.tv_sec = RETRY;
+		tv.tv_usec = 0;
+		if (select(fd+1, &fds, 0, 0, &tv) <= 0) continue;
+
+		/* Process any and all replies */
+		while (got+failed < rrcnt && (rlen = recvfrom(fd, r, 512, 0,
+			(void *)&sa, (socklen_t[1]){sl})) >= 2)
+		{
+			/* Ignore replies from addresses we didn't send to */
+			for (i=0; i<nns; i++) if (!memcmp(ns+i, &sa, sl)) break;
+			if (i==nns) continue;
+
+			/* Compute index of the query from id */
+			i = r[0]*256+r[1] - id & 0xffff;
+			if ((unsigned)i >= rrcnt || !rr[i]) continue;
+
+			/* Interpret the result code */
+			switch (r[3] & 15) {
+			case 0:
+				got++;
+				break;
+			case 3:
+				if (1) errcode = EAI_NONAME; else
+			default:
+				errcode = EAI_FAIL;
+				failed++;
+			}
+
+			/* Mark this record as answered */
+			rr[i] = 0;
+			r += 512;
+		}
+
+		/* Check to see if we have answers to all queries */
+		if (got+failed == rrcnt) break;
+	}
+	close(fd);
+
+	/* Return the number of results, or an error code if none */
+	if (got) return got;
+	return errcode;
+}
+
+static void mkptr4(char *s, const unsigned char *ip)
+{
+	sprintf(s, "%d.%d.%d.%d.in-addr.arpa",
+		ip[3], ip[2], ip[1], ip[0]);
+}
+
+static void mkptr6(char *s, const unsigned char *ip)
+{
+	static const char xdigits[] = "0123456789abcdef";
+	int i;
+	for (i=15; i>=0; i--) {
+		*s++ = xdigits[ip[i]&15]; *s++ = '.';
+		*s++ = xdigits[ip[i]>>4]; *s++ = '.';
+	}
+	strcpy(s, "ip6.arpa");
+}
+
+int __dns_query(unsigned char *r, const void *a, int family, int ptr)
+{
+	char buf[PTR_MAX];
+	int rr[2], rrcnt = 1;
+
+	if (ptr) {
+		if (family == AF_INET6) mkptr6(buf, a);
+		else mkptr4(buf, a);
+		rr[0] = RR_PTR;
+		a = buf;
+	} else if (family == AF_INET6) {
+		rr[0] = RR_AAAA;
+	} else {
+		rr[0] = RR_A;
+		if (family != AF_INET) rr[rrcnt++] = RR_AAAA;
+	}
+
+	return __dns_doqueries(r, a, rr, rrcnt);
+}
+
+
+#define BITOP(a,b,op) \
+ ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
+
+static int decname(char *s, const unsigned char *b, const unsigned char *p)
+{
+	/* Remember jump destinations to detect loops and abort */
+	size_t seen[PACKET_MAX/8/sizeof(size_t)] = { 0 };
+	char *sz = s + HOST_NAME_MAX;
+	const unsigned char *pz = b+512;
+	for (;;) {
+		if (p>=pz) return -1;
+		else if (*p&0xc0) {
+			int j = (p[0]&1) | p[1];
+			if (BITOP(seen, j, &)) return -1;
+			BITOP(seen, j, |=);
+			p = b + j;
+		} else if (*p) {
+			if (p+*p+1>=pz || s+*p>=sz) return -1;
+			memcpy(s, p+1, *p);
+			s += *p+1;
+			p += *p+1;
+			s[-1] = *p ? '.' : 0;
+		} else return 0;
+	}
+}
+
+int __dns_get_rr(void *dest, size_t stride, size_t maxlen, size_t limit, const unsigned char *r, int rr, int dec)
+{
+	int qdcount, ancount;
+	const unsigned char *p;
+	char tmp[256];
+	int found = 0;
+	int len;
+
+	if ((r[3]&15)) return 0;
+	p = r+12;
+	qdcount = r[4]*256 + r[5];
+	ancount = r[6]*256 + r[7];
+	if (qdcount+ancount > 64) return -1;
+	while (qdcount--) {
+		while (p-r < 512 && *p-1U < 127) p++;
+		if (*p>193 || (*p==193 && p[1]>254) || p>r+506)
+			return -1;
+		p += 5 + !!*p;
+	}
+	while (ancount--) {
+		while (p-r < 512 && *p-1U < 127) p++;
+		if (*p>193 || (*p==193 && p[1]>254) || p>r+506)
+			return -1;
+		p += 1 + !!*p;
+		len = p[8]*256 + p[9];
+		if (p+len > r+512) return -1;
+		if (p[1]==rr && len <= maxlen) {
+			if (dec && decname(tmp, r, p+10)<0) return -1;
+			if (dest && limit) {
+				if (dec) strcpy(dest, tmp);
+				else memcpy(dest, p+10, len);
+				dest = (char *)dest + stride;
+				limit--;
+			}
+			found++;
+		}
+		p += 10 + len;
+	}
+	return found;
+}
+
+int __dns_count_addrs(const unsigned char *r, int cnt)
+{
+	int found=0, res, i;
+	static const int p[2][2] = { { 4, RR_A }, { 16, RR_AAAA } };
+
+	while (cnt--) for (i=0; i<2; i++) {
+		res = __dns_get_rr(0, 0, p[i][0], -1, r, p[i][1], 0);
+		if (res < 0) return res;
+		found += res;
+		r += 512;
+	}
+	return found;
+}
diff --git a/src/network/__dns.h b/src/network/__dns.h
new file mode 100644
index 00000000..9a3f7402
--- /dev/null
+++ b/src/network/__dns.h
@@ -0,0 +1,14 @@
+#include <stddef.h>
+
+#define RR_A 1
+#define RR_CNAME 5
+#define RR_PTR 12
+#define RR_AAAA 28
+
+int __dns_count_addrs(const unsigned char *, int);
+int __dns_get_rr(void *, size_t, size_t, size_t, const unsigned char *, int, int);
+
+int __dns_query(unsigned char *, const void *, int, int);
+int __ipparse(void *, int, const char *);
+
+int __dns_doqueries(unsigned char *, const char *, int *, int);
diff --git a/src/network/__ipparse.c b/src/network/__ipparse.c
new file mode 100644
index 00000000..ca9e5890
--- /dev/null
+++ b/src/network/__ipparse.c
@@ -0,0 +1,40 @@
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "__dns.h"
+#include <stdio.h>
+
+int __ipparse(void *dest, int family, const char *s)
+{
+	unsigned char *d = dest;
+	unsigned long a[16] = { 0 };
+	const char *z;
+	int i;
+
+	if (family == AF_INET6) goto not_v4;
+
+	for (i=0; i<4 && *s; i++) {
+		a[i] = strtoul(s, (char **)&z, 0);
+		if (z==s || (*z && *z != '.')) goto not_v4;
+		s=z+1;
+	}
+	switch (i) {
+	case 0:
+		a[1] = a[0] & 0xffffff;
+		a[0] >>= 24;
+	case 1:
+		a[2] = a[1] & 0xffff;
+		a[1] >>= 16;
+	case 2:
+		a[3] = a[2] & 0xff;
+		a[2] >>= 8;
+	}
+	((struct sockaddr_in *)d)->sin_family = AF_INET;
+	d = (void *)&((struct sockaddr_in *)d)->sin_addr;
+	for (i=0; i<4; i++) d[i] = a[i];
+	return 0;
+
+not_v4:
+	return -1;
+}
diff --git a/src/network/accept.c b/src/network/accept.c
new file mode 100644
index 00000000..83704096
--- /dev/null
+++ b/src/network/accept.c
@@ -0,0 +1,14 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+int accept(int fd, struct sockaddr *addr, socklen_t *len)
+{
+	unsigned long args[] = { fd, (unsigned long)addr, (unsigned long)len };
+	int ret;
+	CANCELPT_BEGIN;
+	ret = syscall2(__NR_socketcall, SYS_ACCEPT, (long)args);
+	CANCELPT_END;
+	return ret;
+}
diff --git a/src/network/bind.c b/src/network/bind.c
new file mode 100644
index 00000000..3bef382c
--- /dev/null
+++ b/src/network/bind.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int bind(int fd, const struct sockaddr *addr, socklen_t len)
+{
+	unsigned long args[] = { fd, (unsigned long)addr, len };
+	return syscall2(__NR_socketcall, SYS_BIND, (long)args);
+}
diff --git a/src/network/connect.c b/src/network/connect.c
new file mode 100644
index 00000000..6074122e
--- /dev/null
+++ b/src/network/connect.c
@@ -0,0 +1,14 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+int connect(int fd, const struct sockaddr *addr, socklen_t len)
+{
+	unsigned long args[] = { fd, (unsigned long)addr, len };
+	int ret;
+	CANCELPT_BEGIN;
+	ret = syscall2(__NR_socketcall, SYS_CONNECT, (long)args);
+	CANCELPT_END;
+	return ret;
+}
diff --git a/src/network/dn_expand.c b/src/network/dn_expand.c
new file mode 100644
index 00000000..01b449bb
--- /dev/null
+++ b/src/network/dn_expand.c
@@ -0,0 +1,28 @@
+#include <resolv.h>
+#include <string.h>
+
+#define BITOP(a,b,op) \
+ ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
+
+int dn_expand(unsigned char *b, unsigned char *pz, unsigned char *p, unsigned char *s, int outlen)
+{
+	/* Remember jump destinations to detect loops and abort */
+	size_t seen[512/8/sizeof(size_t)] = { 0 };
+	unsigned char *sz = s + outlen;
+	if (pz-b > 512) return -1;
+	for (;;) {
+		if (p>=pz) return -1;
+		else if (*p&0xc0) {
+			int j = (p[0]&1) | p[1];
+			if (BITOP(seen, j, &)) return -1;
+			BITOP(seen, j, |=);
+			p = b + j;
+		} else if (*p) {
+			if (p+*p+1>=pz || s+*p>=sz) return -1;
+			memcpy(s, p+1, *p);
+			s += *p+1;
+			p += *p+1;
+			s[-1] = *p ? '.' : 0;
+		} else return 0;
+	}
+}
diff --git a/src/network/ent.c b/src/network/ent.c
new file mode 100644
index 00000000..4c2f24b5
--- /dev/null
+++ b/src/network/ent.c
@@ -0,0 +1,26 @@
+#include "libc.h"
+
+void sethostent(int x)
+{
+}
+
+void *gethostent()
+{
+	return 0;
+}
+
+void endhostent(void)
+{
+}
+
+weak_alias(sethostent, setnetent);
+weak_alias(gethostent, getnetent);
+weak_alias(endhostent, endnetent);
+
+weak_alias(sethostent, setservent);
+weak_alias(gethostent, getservent);
+weak_alias(endhostent, endservent);
+
+weak_alias(sethostent, setprotoent);
+weak_alias(gethostent, getprotoent);
+weak_alias(endhostent, endprotoent);
diff --git a/src/network/freeaddrinfo.c b/src/network/freeaddrinfo.c
new file mode 100644
index 00000000..df3798ae
--- /dev/null
+++ b/src/network/freeaddrinfo.c
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+#include <netdb.h>
+
+void freeaddrinfo(struct addrinfo *p)
+{
+	free(p);
+}
diff --git a/src/network/gai_strerror.c b/src/network/gai_strerror.c
new file mode 100644
index 00000000..ea00bed7
--- /dev/null
+++ b/src/network/gai_strerror.c
@@ -0,0 +1,21 @@
+#include <netdb.h>
+
+static const char msgs[] =
+	"Invalid flags\0"
+	"Name does not resolve\0"
+	"Try again\0"
+	"Non-recoverable error\0"
+	"Unrecognized address family or invalid length\0"
+	"Unrecognized socket type\0"
+	"Unrecognized service\0"
+	"Out of memory\0"
+	"System error\0"
+	"Overflow\0"
+	"\0Unknown error";
+
+const char *gai_strerror(int ecode)
+{
+	const char *s;
+	for (s=msgs, ecode++; ecode && *s; ecode++, s++) for (; *s; s++);
+	return *s ? s : s+1;
+}
diff --git a/src/network/getaddrinfo.c b/src/network/getaddrinfo.c
new file mode 100644
index 00000000..90e85f6a
--- /dev/null
+++ b/src/network/getaddrinfo.c
@@ -0,0 +1,224 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include "__dns.h"
+#include "stdio_impl.h"
+
+static int is_valid(const char *host)
+{
+	const unsigned char *s;
+	if (strlen(host)-1 > 254 || mbstowcs(0, host, 0) > 255) return 0;
+	for (s=host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++);
+	return !*s;
+}
+
+#if 0
+static int have_af(int family)
+{
+	struct sockaddr_in6 sin6 = { .sin6_family = family };
+	socklen_t sl = family == AF_INET
+		? sizeof(struct sockaddr_in)
+		: sizeof(struct sockaddr_in6);
+	int sock = socket(family, SOCK_STREAM, 0);
+	int have = !bind(sock, (void *)&sin6, sl);
+	close(sock);
+	return have;
+}
+#endif
+
+#include <stdlib.h>
+#include <netdb.h>
+
+union sa {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+};
+
+struct aibuf {
+	struct addrinfo ai;
+	union sa sa;
+};
+
+/* Extra slots needed for storing canonical name */
+#define EXTRA ((256+sizeof(struct aibuf)-1)/sizeof(struct aibuf))
+
+int getaddrinfo(const char *host, const char *serv, const struct addrinfo *hint, struct addrinfo **res)
+{
+	int flags = hint ? hint->ai_flags : 0;
+	int family = hint ? hint->ai_family : AF_UNSPEC;
+	int type = hint ? hint->ai_socktype : 0;
+	int proto = hint ? hint->ai_protocol : 0;
+	unsigned long port = 0;
+	struct aibuf *buf;
+	union sa sa = {{0}};
+	unsigned char reply[1024];
+	int i, j;
+	//char hostbuf[256];
+	char line[512];
+	FILE *f, _f;
+	unsigned char _buf[64];
+	char *z;
+	int result;
+	int cnt;
+
+	if (host && strlen(host)>255) return EAI_NONAME;
+	if (serv && strlen(serv)>32) return EAI_SERVICE;
+
+	if (type && !proto)
+		proto = type==SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
+	if (!type && proto)
+		type = proto==IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM;
+
+	if (serv) {
+		port = strtoul(serv, &z, 0);
+		if (!*z && port > 65535) return EAI_SERVICE;
+		if (!port) {
+			if (flags & AI_NUMERICSERV) return EAI_SERVICE;
+
+			//f = fopen("/etc/services", "rb");
+			return EAI_SERVICE;
+		}
+		port = htons(port);
+	}
+
+	if (!host) {
+		if (family == AF_UNSPEC) family = AF_INET;
+		buf = calloc(sizeof *buf, 1+EXTRA);
+		if (!buf) return EAI_MEMORY;
+		buf->ai.ai_protocol = proto;
+		buf->ai.ai_socktype = type;
+		buf->ai.ai_addr = (void *)&buf->sa;
+		buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
+		buf->ai.ai_family = family;
+		buf->sa.sin.sin_family = family;
+		buf->sa.sin.sin_port = port;
+		if (!(flags & AI_PASSIVE)) {
+			if (family == AF_INET) {
+				0[(uint8_t*)&buf->sa.sin.sin_addr.s_addr]=127;
+				3[(uint8_t*)&buf->sa.sin.sin_addr.s_addr]=1;
+			} else buf[0].sa.sin6.sin6_addr.s6_addr[15] = 1;
+		}
+		*res = &buf->ai;
+		return 0;
+	}
+
+	/* Try as a numeric address */
+	if (__ipparse(&sa, family, host) >= 0) {
+		buf = calloc(sizeof *buf, 1+EXTRA);
+		if (!buf) return EAI_MEMORY;
+		family = sa.sin.sin_family;
+		buf->ai.ai_protocol = proto;
+		buf->ai.ai_socktype = type;
+		buf->ai.ai_addr = (void *)&buf->sa;
+		buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
+		buf->ai.ai_family = family;
+		buf->sa = sa;
+		buf->sa.sin.sin_port = port;
+		*res = &buf->ai;
+		return 0;
+	}
+
+	if (flags & AI_NUMERICHOST) return EAI_NONAME;
+
+	f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf);
+	if (f) while (fgets(line, sizeof line, f)) {
+		char *p;
+		size_t l = strlen(host);
+
+		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
+		for(p=line+1; (p=strstr(p, host)) &&
+			(!isspace(p[-1]) || !isspace(p[l])); p++);
+		if (!p) continue;
+		__fclose_ca(f);
+
+		/* Isolate IP address to parse */
+		for (p=line; *p && !isspace(*p); p++);
+		*p++ = 0;
+		if (__ipparse(&sa, family, line) < 0) return EAI_NONAME;
+
+		/* Allocate and fill result buffer */
+		buf = calloc(sizeof *buf, 1+EXTRA);
+		if (!buf) return EAI_MEMORY;
+		family = sa.sin.sin_family;
+		buf->ai.ai_protocol = proto;
+		buf->ai.ai_socktype = type;
+		buf->ai.ai_addr = (void *)&buf->sa;
+		buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
+		buf->ai.ai_family = family;
+		buf->sa = sa;
+		buf->sa.sin.sin_port = port;
+
+		/* Extract first name as canonical name */
+		for (; *p && isspace(*p); p++);
+		buf->ai.ai_canonname = (void *)(buf+1);
+		snprintf(buf->ai.ai_canonname, 256, "%s", p);
+		for (p=buf->ai.ai_canonname; *p && !isspace(*p); p++);
+		*p = 0;
+		if (!is_valid(buf->ai.ai_canonname))
+			buf->ai.ai_canonname = 0;
+
+		*res = &buf->ai;
+		return 0;
+	}
+	if (f) __fclose_ca(f);
+
+#if 0
+	f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf);
+	if (f) while (fgets(line, sizeof line, f)) {
+		if (!isspace(line[10]) || (strncmp(line, "search", 6)
+			&& strncmp(line, "domain", 6))) continue;
+	}
+	if (f) __fclose_ca(f);
+#endif
+
+	/* Perform one or more DNS queries for host */
+	memset(reply, 0, sizeof reply);
+	result = __dns_query(reply, host, family, 0);
+	if (result < 0) return result;
+
+	cnt = __dns_count_addrs(reply, result);
+	if (cnt <= 0) return EAI_NONAME;
+
+	buf = calloc(sizeof *buf, cnt+EXTRA);
+	if (!buf) return EAI_MEMORY;
+
+	i = 0;
+	if (family != AF_INET6) {
+		j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply, RR_A, 0);
+		while (j--) buf[i++].sa.sin.sin_family = AF_INET;
+	}
+	if (family != AF_INET) {
+		j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply, RR_AAAA, 0);
+		while (j--) buf[i++].sa.sin.sin_family = AF_INET6;
+	}
+	if (result>1) {
+		j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply+512, RR_A, 0);
+		while (j--) buf[i++].sa.sin.sin_family = AF_INET;
+		j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply+512, RR_AAAA, 0);
+		while (j--) buf[i++].sa.sin.sin_family = AF_INET6;
+	}
+
+	if (__dns_get_rr((void *)&buf[cnt], 0, 256, 1, reply, RR_CNAME, 1) < 0)
+		strcpy((void *)&buf[cnt], host);
+
+	for (i=0; i<cnt; i++) {
+		buf[i].ai.ai_protocol = proto;
+		buf[i].ai.ai_socktype = type;
+		buf[i].ai.ai_addr = (void *)&buf[i].sa;
+		buf[i].ai.ai_addrlen = buf[i].sa.sin.sin_family==AF_INET6
+			? sizeof sa.sin6 : sizeof sa.sin;
+		buf[i].ai.ai_family = buf[i].sa.sin.sin_family;
+		buf[i].sa.sin.sin_port = port;
+		buf[i].ai.ai_next = &buf[i+1].ai;
+		buf[i].ai.ai_canonname = (void *)&buf[cnt];
+	}
+	buf[cnt-1].ai.ai_next = 0;
+	*res = &buf->ai;
+
+	return 0;
+}
diff --git a/src/network/gethostbyaddr.c b/src/network/gethostbyaddr.c
new file mode 100644
index 00000000..51e1c569
--- /dev/null
+++ b/src/network/gethostbyaddr.c
@@ -0,0 +1,15 @@
+#define _GNU_SOURCE
+
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+
+struct hostent *gethostbyaddr(const void *a, socklen_t l, int af)
+{
+	static struct hostent h;
+	static long buf[512/sizeof(long)];
+	struct hostent *res;
+	if (gethostbyaddr_r(a, l, af, &h,
+		(void *)buf, sizeof buf, &res, &h_errno)) return 0;
+	return &h;
+}
diff --git a/src/network/gethostbyaddr_r.c b/src/network/gethostbyaddr_r.c
new file mode 100644
index 00000000..cdb1d503
--- /dev/null
+++ b/src/network/gethostbyaddr_r.c
@@ -0,0 +1,71 @@
+#define _GNU_SOURCE
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <inttypes.h>
+
+int gethostbyaddr_r(const void *a, socklen_t l, int af,
+	struct hostent *h, char *buf, size_t buflen,
+	struct hostent **res, int *err)
+{
+	union {
+		struct sockaddr_in sin;
+		struct sockaddr_in6 sin6;
+	} sa = { .sin.sin_family = af };
+	socklen_t sl = af==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
+	int i;
+
+	/* Load address argument into sockaddr structure */
+	if (af==AF_INET6 && l==16) memcpy(&sa.sin6.sin6_addr, a, 16);
+	else if (af==AF_INET && l==4) memcpy(&sa.sin.sin_addr, a, 4);
+	else {
+		*err = NO_RECOVERY;
+		return -1;
+	}
+
+	/* Align buffer and check for space for pointers and ip address */
+	i = (uintptr_t)buf & sizeof(char *)-1;
+	if (!i) i = sizeof(char *);
+	if (buflen <= 5*sizeof(char *)-i + l) {
+		errno = ERANGE;
+		return -1;
+	}
+	buf += sizeof(char *)-i;
+	buflen -= 5*sizeof(char *)-i + l;
+
+	h->h_addr_list = (void *)buf;
+	buf += 2*sizeof(char *);
+	h->h_aliases = (void *)buf;
+	buf += 2*sizeof(char *);
+
+	h->h_addr_list[0] = buf;
+	memcpy(h->h_addr_list[0], a, l);
+	buf += l;
+	h->h_addr_list[1] = 0;
+	h->h_aliases[0] = buf;
+	h->h_aliases[1] = 0;
+
+	switch (getnameinfo((void *)&sa, sl, buf, buflen, 0, 0, 0)) {
+	case EAI_AGAIN:
+		*err = TRY_AGAIN;
+		return -1;
+	case EAI_OVERFLOW:
+		errno = ERANGE;
+	default:
+	case EAI_MEMORY:
+	case EAI_SYSTEM:
+	case EAI_FAIL:
+		*err = NO_RECOVERY;
+		return -1;
+	case 0:
+		break;
+	}
+
+	h->h_addrtype = af;
+	h->h_name = h->h_aliases[0];
+	*res = h;
+	return 0;
+}
diff --git a/src/network/gethostbyname.c b/src/network/gethostbyname.c
new file mode 100644
index 00000000..5088a51e
--- /dev/null
+++ b/src/network/gethostbyname.c
@@ -0,0 +1,63 @@
+#define _GNU_SOURCE
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+
+struct hostent *gethostbyname(const char *name)
+{
+	return gethostbyname2(name, AF_INET);
+}
+
+#if 0
+struct hostent *gethostbyname(const char *name)
+{
+	static struct hostent h;
+	static char *h_aliases[3];
+	static char h_canon[256];
+	static char *h_addr_list[10];
+	static char h_addr_data[10][4];
+	static const struct addrinfo hint = {
+		.ai_family = AF_INET, .ai_flags = AI_CANONNAME
+	};
+	struct addrinfo *ai, *p;
+	int i;
+
+	switch (getaddrinfo(name, 0, &hint, &ai)) {
+	case EAI_NONAME:
+		h_errno = HOST_NOT_FOUND;
+		break;
+	case EAI_AGAIN:
+		h_errno = TRY_AGAIN;
+		break;
+	case EAI_FAIL:
+		h_errno = NO_RECOVERY;
+		break;
+	default:
+	case EAI_MEMORY:
+	case EAI_SYSTEM:
+		h_errno = NO_DATA;
+		break;
+	case 0:
+		break;
+	}
+
+	strcpy(h_canon, ai->ai_canonname);
+	h.h_name = h_canon;
+	h.h_aliases = h_aliases;
+	h.h_aliases[0] = h_canon;
+	h.h_aliases[1] = strcmp(h_canon, name) ? (char *)name : 0;
+	h.h_length = 4;
+	h.h_addr_list = h_addr_list;
+	for (i=0, p=ai; i<sizeof h_addr_data/4 && p; i++, p=p->ai_next) {
+		h.h_addr_list[i] = h_addr_data[i];
+		memcpy(h.h_addr_list[i],
+			&((struct sockaddr_in *)p->ai_addr)->sin_addr, 4);
+	}
+	h.h_addr_list[i] = 0;
+	h.h_addrtype = AF_INET;
+	freeaddrinfo(ai);
+	return &h;
+}
+#endif
diff --git a/src/network/gethostbyname2.c b/src/network/gethostbyname2.c
new file mode 100644
index 00000000..9fbe2647
--- /dev/null
+++ b/src/network/gethostbyname2.c
@@ -0,0 +1,16 @@
+#define _GNU_SOURCE
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+
+struct hostent *gethostbyname2(const char *name, int af)
+{
+	static struct hostent h;
+	static long buf[512/sizeof(long)];
+	struct hostent *res;
+	if (gethostbyname2_r(name, af, &h,
+		(void *)buf, sizeof buf, &res, &h_errno)) return 0;
+	return &h;
+}
diff --git a/src/network/gethostbyname2_r.c b/src/network/gethostbyname2_r.c
new file mode 100644
index 00000000..c2ed75b4
--- /dev/null
+++ b/src/network/gethostbyname2_r.c
@@ -0,0 +1,99 @@
+#define _GNU_SOURCE
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <inttypes.h>
+
+int gethostbyname2_r(const char *name, int af,
+	struct hostent *h, char *buf, size_t buflen,
+	struct hostent **res, int *err)
+{
+	struct addrinfo hint = {
+		.ai_family = af==AF_INET6 ? af : AF_INET,
+		.ai_flags = AI_CANONNAME
+	};
+	struct addrinfo *ai, *p;
+	int i;
+	size_t need;
+	const char *canon;
+
+	af = hint.ai_family;
+
+	/* Align buffer */
+	i = (uintptr_t)buf & sizeof(char *)-1;
+	if (i) {
+		if (buflen < sizeof(char *)-i) {
+			errno = ERANGE;
+			return -1;
+		}
+		buf += sizeof(char *)-i;
+		buflen -= sizeof(char *)-i;
+	}
+
+	getaddrinfo(name, 0, &hint, &ai);
+	switch (getaddrinfo(name, 0, &hint, &ai)) {
+	case EAI_NONAME:
+		*err = HOST_NOT_FOUND;
+		return -1;
+	case EAI_AGAIN:
+		*err = TRY_AGAIN;
+		return -1;
+	default:
+	case EAI_MEMORY:
+	case EAI_SYSTEM:
+	case EAI_FAIL:
+		*err = NO_RECOVERY;
+		return -1;
+	case 0:
+		break;
+	}
+
+	h->h_addrtype = af;
+	h->h_length = af==AF_INET6 ? 16 : 4;
+
+	canon = ai->ai_canonname ? ai->ai_canonname : name;
+	need = 4*sizeof(char *);
+	for (i=0, p=ai; p; i++, p=p->ai_next)
+		need += sizeof(char *) + h->h_length;
+	need += strlen(name)+1;
+	need += strlen(canon)+1;
+
+	if (need > buflen) {
+		freeaddrinfo(ai);
+		errno = ERANGE;
+		return -1;
+	}
+
+	h->h_aliases = (void *)buf;
+	buf += 3*sizeof(char *);
+	h->h_addr_list = (void *)buf;
+	buf += (i+1)*sizeof(char *);
+
+	h->h_name = h->h_aliases[0] = buf;
+	strcpy(h->h_name, canon);
+	buf += strlen(h->h_name)+1;
+
+	if (strcmp(h->h_name, name)) {
+		h->h_aliases[1] = buf;
+		strcpy(h->h_aliases[1], name);
+		buf += strlen(h->h_aliases[1])+1;
+	} else h->h_aliases[1] = 0;
+
+	h->h_aliases[2] = 0;
+
+	for (i=0, p=ai; p; i++, p=p->ai_next) {
+		h->h_addr_list[i] = (void *)buf;
+		buf += h->h_length;
+		memcpy(h->h_addr_list[i],
+			&((struct sockaddr_in *)p->ai_addr)->sin_addr,
+			h->h_length);
+	}
+	h->h_addr_list[i] = 0;
+
+	*res = h;
+	freeaddrinfo(ai);
+	return 0;
+}
diff --git a/src/network/gethostbyname_r.c b/src/network/gethostbyname_r.c
new file mode 100644
index 00000000..cd872541
--- /dev/null
+++ b/src/network/gethostbyname_r.c
@@ -0,0 +1,11 @@
+#define _GNU_SOURCE
+
+#include <sys/socket.h>
+#include <netdb.h>
+
+int gethostbyname_r(const char *name,
+	struct hostent *h, char *buf, size_t buflen,
+	struct hostent **res, int *err)
+{
+	return gethostbyname2_r(name, AF_INET, h, buf, buflen, res, err);
+}
diff --git a/src/network/getnameinfo.c b/src/network/getnameinfo.c
new file mode 100644
index 00000000..0763ca88
--- /dev/null
+++ b/src/network/getnameinfo.c
@@ -0,0 +1,54 @@
+#include <netdb.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "__dns.h"
+
+int getnameinfo(const struct sockaddr *sa, socklen_t sl,
+	char *node, socklen_t nodelen,
+	char *serv, socklen_t servlen,
+	int flags)
+{
+	char buf[256];
+	unsigned char reply[512];
+	int af = sa->sa_family;
+	unsigned char *a;
+
+	switch (af) {
+	case AF_INET:
+		a = (void *)&((struct sockaddr_in *)sa)->sin_addr;
+		if (sl != sizeof(struct sockaddr_in)) return EAI_FAMILY;
+		break;
+	case AF_INET6:
+		a = (void *)&((struct sockaddr_in6 *)sa)->sin6_addr;
+		if (sl != sizeof(struct sockaddr_in6)) return EAI_FAMILY;
+		break;
+	default:
+		return EAI_FAMILY;
+	}
+
+	if (node && nodelen) {
+		if ((flags & NI_NUMERICHOST)
+			|| __dns_query(reply, a, af, 1) <= 0
+			|| __dns_get_rr(buf, 0, 256, 1, reply, RR_PTR, 1) <= 0)
+		{
+			if (flags & NI_NAMEREQD) return EAI_NONAME;
+			inet_ntop(af, a, buf, sizeof buf);
+		}
+		if (strlen(buf) >= nodelen) return EAI_OVERFLOW;
+		strcpy(node, buf);
+	}
+
+	if (serv && servlen) {
+		if (snprintf(buf, sizeof buf, "%d",
+			ntohs(((struct sockaddr_in *)sa)->sin_port))>=servlen)
+			return EAI_OVERFLOW;
+		strcpy(serv, buf);
+	}
+
+	return 0;
+}
diff --git a/src/network/getpeername.c b/src/network/getpeername.c
new file mode 100644
index 00000000..7ecbe375
--- /dev/null
+++ b/src/network/getpeername.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int getpeername(int fd, struct sockaddr *addr, socklen_t *len)
+{
+	unsigned long args[] = { fd, (unsigned long)addr, (unsigned long)len };
+	return syscall2(__NR_socketcall, SYS_GETPEERNAME, (long)args);
+}
diff --git a/src/network/getservbyname.c b/src/network/getservbyname.c
new file mode 100644
index 00000000..0b00ce11
--- /dev/null
+++ b/src/network/getservbyname.c
@@ -0,0 +1,12 @@
+#define _GNU_SOURCE
+#include <netdb.h>
+
+struct servent *getservbyname(const char *name, const char *prots)
+{
+	static struct servent se;
+	static long buf[32/sizeof(long)];
+	struct servent *res;
+	if (getservbyname_r(name, prots, &se, (void *)buf, sizeof buf, &res))
+		return 0;
+	return &se;
+}
diff --git a/src/network/getservbyname_r.c b/src/network/getservbyname_r.c
new file mode 100644
index 00000000..5c025150
--- /dev/null
+++ b/src/network/getservbyname_r.c
@@ -0,0 +1,41 @@
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+
+int getservbyname_r(const char *name, const char *prots,
+	struct servent *se, char *buf, size_t buflen, struct servent **res)
+{
+	struct addrinfo *ai, hint = { .ai_family = AF_INET };
+	int i;
+
+	/* Align buffer */
+	i = (uintptr_t)buf & sizeof(char *)-1;
+	if (!i) i = sizeof(char *);
+	if (buflen < 3*sizeof(char *)-i) {
+		errno = ERANGE;
+		return -1;
+	}
+	buf += sizeof(char *)-i;
+	buflen -= sizeof(char *)-i;
+
+	if (!strcmp(prots, "tcp")) hint.ai_protocol = IPPROTO_TCP;
+	else if (!strcmp(prots, "udp")) hint.ai_protocol = IPPROTO_UDP;
+	else return -1;
+
+	if (getaddrinfo(0, name, &hint, &ai) < 0) return -1;
+
+	se->s_name = (char *)name;
+	se->s_aliases = (void *)buf;
+	se->s_aliases[0] = se->s_name;
+	se->s_aliases[1] = 0;
+	se->s_port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
+	se->s_proto = (char *)prots;
+
+	freeaddrinfo(ai);
+	*res = se;
+	return 0;
+}
diff --git a/src/network/getservbyport.c b/src/network/getservbyport.c
new file mode 100644
index 00000000..c9ecbb11
--- /dev/null
+++ b/src/network/getservbyport.c
@@ -0,0 +1,12 @@
+#define _GNU_SOURCE
+#include <netdb.h>
+
+struct servent *getservbyport(int port, const char *prots)
+{
+	static struct servent se;
+	static long buf[32/sizeof(long)];
+	struct servent *res;
+	if (getservbyport_r(port, prots, &se, (void *)buf, sizeof buf, &res))
+		return 0;
+	return &se;
+}
diff --git a/src/network/getservbyport_r.c b/src/network/getservbyport_r.c
new file mode 100644
index 00000000..004a6168
--- /dev/null
+++ b/src/network/getservbyport_r.c
@@ -0,0 +1,43 @@
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+
+int getservbyport_r(int port, const char *prots,
+	struct servent *se, char *buf, size_t buflen, struct servent **res)
+{
+	int i;
+	struct sockaddr_in sin = {
+		.sin_family = AF_INET,
+		.sin_port = port,
+	};
+
+	/* Align buffer */
+	i = (uintptr_t)buf & sizeof(char *)-1;
+	if (!i) i = sizeof(char *);
+	if (buflen < 3*sizeof(char *)-i) {
+		errno = ERANGE;
+		return -1;
+	}
+	buf += sizeof(char *)-i;
+	buflen -= sizeof(char *)-i;
+
+	if (strcmp(prots, "tcp") && strcmp(prots, "udp")) return -1;
+
+	se->s_port = port;
+	se->s_proto = (char *)prots;
+	se->s_aliases = (void *)buf;
+	buf += 2*sizeof(char *);
+	buflen -= 2*sizeof(char *);
+	se->s_aliases[1] = 0;
+	se->s_aliases[0] = se->s_name = buf;
+
+	if (getnameinfo((void *)&sin, sizeof sin, 0, 0, buf, buflen, 
+		strcmp(prots, "udp") ? 0 : NI_DGRAM) < 0) return -1;
+
+	*res = se;
+	return 0;
+}
diff --git a/src/network/getsockname.c b/src/network/getsockname.c
new file mode 100644
index 00000000..4b1002f8
--- /dev/null
+++ b/src/network/getsockname.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int getsockname(int fd, struct sockaddr *addr, socklen_t *len)
+{
+	unsigned long args[] = { fd, (unsigned long)addr, (unsigned long)len };
+	return syscall2(__NR_socketcall, SYS_GETSOCKNAME, (long)args);
+}
diff --git a/src/network/getsockopt.c b/src/network/getsockopt.c
new file mode 100644
index 00000000..8c818863
--- /dev/null
+++ b/src/network/getsockopt.c
@@ -0,0 +1,13 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
+{
+	unsigned long args[] = {
+		fd, level, optname,
+		(unsigned long)optval,
+		(unsigned long)optlen
+	};
+	return syscall2(__NR_socketcall, SYS_GETSOCKOPT, (long)args);
+}
diff --git a/src/network/h_errno.c b/src/network/h_errno.c
new file mode 100644
index 00000000..73ead046
--- /dev/null
+++ b/src/network/h_errno.c
@@ -0,0 +1 @@
+int h_errno;
diff --git a/src/network/hstrerror.c b/src/network/hstrerror.c
new file mode 100644
index 00000000..b7a6ab6c
--- /dev/null
+++ b/src/network/hstrerror.c
@@ -0,0 +1,16 @@
+#define _GNU_SOURCE
+#include <netdb.h>
+
+static const char msgs[] =
+	"Host not found\0"
+	"Try again\0"
+	"Non-recoverable error\0"
+	"Address not available\0"
+	"\0Unknown error";
+
+const char *hstrerror(int ecode)
+{
+	const char *s;
+	for (s=msgs, ecode--; ecode && *s; ecode--, s++) for (; *s; s++);
+	return *s ? s : s+1;
+}
diff --git a/src/network/htonl.c b/src/network/htonl.c
new file mode 100644
index 00000000..b21dace0
--- /dev/null
+++ b/src/network/htonl.c
@@ -0,0 +1,10 @@
+#include <netinet/in.h>
+
+uint32_t htonl(uint32_t n)
+{
+	union {
+		uint8_t b[4];
+		uint32_t i;
+	} u = { { n>>24, n>>16, n>>8, n } };
+	return u.i;
+}
diff --git a/src/network/htons.c b/src/network/htons.c
new file mode 100644
index 00000000..522504a5
--- /dev/null
+++ b/src/network/htons.c
@@ -0,0 +1,10 @@
+#include <netinet/in.h>
+
+uint16_t htons(uint16_t n)
+{
+	union {
+		uint8_t b[2];
+		uint16_t s;
+	} u = { { n>>8, n } };
+	return u.s;
+}
diff --git a/src/network/in6addr_any.c b/src/network/in6addr_any.c
new file mode 100644
index 00000000..995387fa
--- /dev/null
+++ b/src/network/in6addr_any.c
@@ -0,0 +1,3 @@
+#include <netinet/in.h>
+
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
diff --git a/src/network/in6addr_loopback.c b/src/network/in6addr_loopback.c
new file mode 100644
index 00000000..b96005b8
--- /dev/null
+++ b/src/network/in6addr_loopback.c
@@ -0,0 +1,3 @@
+#include <netinet/in.h>
+
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
diff --git a/src/network/inet_addr.c b/src/network/inet_addr.c
new file mode 100644
index 00000000..84137281
--- /dev/null
+++ b/src/network/inet_addr.c
@@ -0,0 +1,11 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "__dns.h"
+
+in_addr_t inet_addr(const char *p)
+{
+	struct sockaddr_in sin;
+	if (__ipparse(&sin, AF_INET, p)) return -1;
+	return sin.sin_addr.s_addr;
+}
diff --git a/src/network/inet_aton.c b/src/network/inet_aton.c
new file mode 100644
index 00000000..ea4ee165
--- /dev/null
+++ b/src/network/inet_aton.c
@@ -0,0 +1,7 @@
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+int inet_aton(const char *cp, struct in_addr *inp)
+{
+	return inet_pton(AF_INET, cp, (void *)inp) > 0;
+}
diff --git a/src/network/inet_ntoa.c b/src/network/inet_ntoa.c
new file mode 100644
index 00000000..71411e0b
--- /dev/null
+++ b/src/network/inet_ntoa.c
@@ -0,0 +1,10 @@
+#include <arpa/inet.h>
+#include <stdio.h>
+
+char *inet_ntoa(struct in_addr in)
+{
+	static char buf[16];
+	unsigned char *a = (void *)&in;
+	snprintf(buf, sizeof buf, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
+	return buf;
+}
diff --git a/src/network/inet_ntop.c b/src/network/inet_ntop.c
new file mode 100644
index 00000000..3e8a6db0
--- /dev/null
+++ b/src/network/inet_ntop.c
@@ -0,0 +1,48 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+const char *inet_ntop(int af, const void *a0, char *s, socklen_t l)
+{
+	const unsigned char *a = a0;
+	int i, j, max, best;
+	char buf[100];
+
+	switch (af) {
+	case AF_INET:
+		if (snprintf(s, l, "%d.%d.%d.%d", a[0],a[1],a[2],a[3]) < l)
+			return s;
+		break;
+	case AF_INET6:
+		memset(buf, 'x', sizeof buf);
+		buf[sizeof buf-1]=0;
+		snprintf(buf, sizeof buf, 
+			"%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x",
+			a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],
+			a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]);
+		/* Replace longest /(^0|:)[:0]{2,}/ with "::" */
+		for (i=best=0, max=2; buf[i]; i++) {
+			if (i && buf[i] != ':') continue;
+			j = strspn(buf+i, ":0");
+			if (j>max) best=i, max=j;
+		}
+		if (max>2) {
+			buf[best] = buf[best+1] = ':';
+			strcpy(buf+best+2, buf+best+max);
+		}
+		if (strlen(buf) < l) {
+			strcpy(s, buf);
+			return s;
+		}
+		break;
+	default:
+		errno = EAFNOSUPPORT;
+		return 0;
+	}
+	errno = ENOSPC;
+	return 0;
+}
diff --git a/src/network/inet_pton.c b/src/network/inet_pton.c
new file mode 100644
index 00000000..349c4025
--- /dev/null
+++ b/src/network/inet_pton.c
@@ -0,0 +1,31 @@
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include "__dns.h"
+
+int inet_pton(int af, const char *s, void *a0)
+{
+	unsigned char *a = a0;
+	const char *z;
+	unsigned long x;
+	int i;
+
+	/* Reimplement this because inet_pton cannot accept special v4 forms */
+	if (af==AF_INET) {
+		for (i=0; i<4 && *s; i++) {
+			a[i] = x = strtoul(s, (char **)&z, 10);
+			if (!isdigit(*s) || z==s || (*z && *z != '.') || x>255)
+				return 0;
+			s=z+1;
+		}
+		return 0;
+	} else if (af==AF_INET6) {
+		return !__ipparse(a, AF_INET6, s);
+	}
+
+	errno = EAFNOSUPPORT;
+	return 0;
+}
diff --git a/src/network/listen.c b/src/network/listen.c
new file mode 100644
index 00000000..7c8c1a8a
--- /dev/null
+++ b/src/network/listen.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int listen(int fd, int backlog)
+{
+	unsigned long args[] = { fd, backlog };
+	return syscall2(__NR_socketcall, SYS_LISTEN, (long)args);
+}
diff --git a/src/network/ntohl.c b/src/network/ntohl.c
new file mode 100644
index 00000000..64379196
--- /dev/null
+++ b/src/network/ntohl.c
@@ -0,0 +1,10 @@
+#include <netinet/in.h>
+
+uint32_t ntohl(uint32_t n)
+{
+	union {
+		uint32_t i;
+		uint8_t b[4];
+	} u = { n };
+	return (u.b[0]<<24) | (u.b[1]<<16) | (u.b[2]<<8) | u.b[3];
+}
diff --git a/src/network/ntohs.c b/src/network/ntohs.c
new file mode 100644
index 00000000..3544a479
--- /dev/null
+++ b/src/network/ntohs.c
@@ -0,0 +1,10 @@
+#include <netinet/in.h>
+
+uint16_t ntohs(uint16_t n)
+{
+	union {
+		uint16_t s;
+		uint8_t b[2];
+	} u = { n };
+	return (u.b[0]<<8) | u.b[1];
+}
diff --git a/src/network/proto.c b/src/network/proto.c
new file mode 100644
index 00000000..8c25c53a
--- /dev/null
+++ b/src/network/proto.c
@@ -0,0 +1,58 @@
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+
+/* do we really need all these?? */
+
+static int idx;
+static const unsigned char protos[][6] = {
+	"\000ip",
+	"\001icmp",
+	"\002igmp",
+	"\003ggp",
+	"\006tcp",
+	"\014pup",
+	"\021udp",
+	"\026idp",
+	"\377raw"
+	"\0\0"
+};
+
+void endprotoent(void)
+{
+	idx = 0;
+}
+
+void setprotoent(int stayopen)
+{
+	idx = 0;
+}
+
+struct protoent *getprotoent(void)
+{
+	static struct protoent p;
+	static const char *aliases;
+	if (!protos[idx][1]) return NULL;
+	p.p_proto = protos[idx][0];
+	p.p_name = (char *)protos[idx++]+1;
+	p.p_aliases = (char **)&aliases;
+	return &p;
+}
+
+struct protoent *getprotobyname(const char *name)
+{
+	struct protoent *p;
+	endprotoent();
+	do p = getprotoent();
+	while (p && strcmp(name, p->p_name));
+	return p;
+}
+
+struct protoent *getprotobynumber(int num)
+{
+	struct protoent *p;
+	endprotoent();
+	do p = getprotoent();
+	while (p && p->p_proto != num);
+	return p;
+}
diff --git a/src/network/recv.c b/src/network/recv.c
new file mode 100644
index 00000000..521a4b19
--- /dev/null
+++ b/src/network/recv.c
@@ -0,0 +1,14 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+ssize_t recv(int fd, void *buf, size_t len, int flags)
+{
+	unsigned long args[] = { fd, (unsigned long)buf, len, flags };
+	ssize_t r;
+	CANCELPT_BEGIN;
+	r = syscall2(__NR_socketcall, SYS_RECV, (long)args);
+	CANCELPT_END;
+	return r;
+}
diff --git a/src/network/recvfrom.c b/src/network/recvfrom.c
new file mode 100644
index 00000000..df50114b
--- /dev/null
+++ b/src/network/recvfrom.c
@@ -0,0 +1,17 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+ssize_t recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *alen)
+{
+	unsigned long args[] = {
+		fd, (unsigned long)buf, len, flags,
+		(unsigned long)addr, (unsigned long)alen
+	};
+	ssize_t r;
+	CANCELPT_BEGIN;
+	r = syscall2(__NR_socketcall, SYS_RECVFROM, (long)args);
+	CANCELPT_END;
+	return r;
+}
diff --git a/src/network/recvmsg.c b/src/network/recvmsg.c
new file mode 100644
index 00000000..ead16f9c
--- /dev/null
+++ b/src/network/recvmsg.c
@@ -0,0 +1,14 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+ssize_t recvmsg(int fd, struct msghdr *msg, int flags)
+{
+	unsigned long args[] = { fd, (unsigned long)msg, flags };
+	ssize_t r;
+	CANCELPT_BEGIN;
+	r = syscall2(__NR_socketcall, SYS_RECVMSG, (long)args);
+	CANCELPT_END;
+	return r;
+}
diff --git a/src/network/res_init.c b/src/network/res_init.c
new file mode 100644
index 00000000..cbd5b155
--- /dev/null
+++ b/src/network/res_init.c
@@ -0,0 +1,4 @@
+int res_init()
+{
+	return 0;
+}
diff --git a/src/network/res_query.c b/src/network/res_query.c
new file mode 100644
index 00000000..4ebeb102
--- /dev/null
+++ b/src/network/res_query.c
@@ -0,0 +1,20 @@
+#include <netdb.h>
+#include "__dns.h"
+
+int res_query(const char *name, int class, int type, unsigned char *dest, int len)
+{
+	if (class != 1 || len < 512)
+		return -1;
+	switch(__dns_doqueries(dest, name, &type, 1)) {
+	case EAI_NONAME:
+		h_errno = HOST_NOT_FOUND;
+		return -1;
+	case EAI_AGAIN:
+		h_errno = TRY_AGAIN;
+		return -1;
+	case EAI_FAIL:
+		h_errno = NO_RECOVERY;
+		return -1;
+	}
+	return 512;
+}
diff --git a/src/network/send.c b/src/network/send.c
new file mode 100644
index 00000000..d72fb03c
--- /dev/null
+++ b/src/network/send.c
@@ -0,0 +1,14 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+ssize_t send(int fd, const void *buf, size_t len, int flags)
+{
+	unsigned long args[] = { fd, (unsigned long)buf, len, flags };
+	ssize_t r;
+	CANCELPT_BEGIN;
+	r = syscall2(__NR_socketcall, SYS_SEND, (long)args);
+	CANCELPT_END;
+	return r;
+}
diff --git a/src/network/sendmsg.c b/src/network/sendmsg.c
new file mode 100644
index 00000000..ea2fe482
--- /dev/null
+++ b/src/network/sendmsg.c
@@ -0,0 +1,14 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+ssize_t sendmsg(int fd, const struct msghdr *msg, int flags)
+{
+	unsigned long args[] = { fd, (unsigned long)msg, flags };
+	ssize_t r;
+	CANCELPT_BEGIN;
+	r = syscall2(__NR_socketcall, SYS_SENDMSG, (long)args);
+	CANCELPT_END;
+	return r;
+}
diff --git a/src/network/sendto.c b/src/network/sendto.c
new file mode 100644
index 00000000..5d224b0b
--- /dev/null
+++ b/src/network/sendto.c
@@ -0,0 +1,17 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+#include "libc.h"
+
+ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t alen)
+{
+	unsigned long args[] = {
+		fd, (unsigned long)buf, len, flags,
+		(unsigned long)addr, alen
+	};
+	ssize_t r;
+	CANCELPT_BEGIN;
+	r = syscall2(__NR_socketcall, SYS_SENDTO, (long)args);
+	CANCELPT_END;
+	return r;
+}
diff --git a/src/network/serv.c b/src/network/serv.c
new file mode 100644
index 00000000..5ade6ad1
--- /dev/null
+++ b/src/network/serv.c
@@ -0,0 +1,16 @@
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+
+void endservent(void)
+{
+}
+
+void setservent(int stayopen)
+{
+}
+
+struct servent *getservent(void)
+{
+	return 0;
+}
diff --git a/src/network/setsockopt.c b/src/network/setsockopt.c
new file mode 100644
index 00000000..b50303b8
--- /dev/null
+++ b/src/network/setsockopt.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
+{
+	unsigned long args[] = { fd, level, optname, (unsigned long)optval, optlen };
+	return syscall2(__NR_socketcall, SYS_SETSOCKOPT, (long)args);
+}
diff --git a/src/network/shutdown.c b/src/network/shutdown.c
new file mode 100644
index 00000000..91950c8a
--- /dev/null
+++ b/src/network/shutdown.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int shutdown(int fd, int how)
+{
+	unsigned long args[] = { fd, how };
+	return syscall2(__NR_socketcall, SYS_SHUTDOWN, (long)args);
+}
diff --git a/src/network/sockatmark.c b/src/network/sockatmark.c
new file mode 100644
index 00000000..5328a855
--- /dev/null
+++ b/src/network/sockatmark.c
@@ -0,0 +1,11 @@
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include "socketcall.h"
+
+int sockatmark(int s)
+{
+	int ret;
+	if (ioctl(s, SIOCATMARK, &ret) < 0)
+		return -1;
+	return ret;
+}
diff --git a/src/network/socket.c b/src/network/socket.c
new file mode 100644
index 00000000..afaeb411
--- /dev/null
+++ b/src/network/socket.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int socket(int domain, int type, int protocol)
+{
+	unsigned long args[] = { domain, type, protocol };
+	return syscall2(__NR_socketcall, SYS_SOCKET, (long)args);
+}
diff --git a/src/network/socketcall.h b/src/network/socketcall.h
new file mode 100644
index 00000000..9ae98587
--- /dev/null
+++ b/src/network/socketcall.h
@@ -0,0 +1,24 @@
+#define SYS_SOCKET      1
+#define SYS_BIND        2
+#define SYS_CONNECT     3
+#define SYS_LISTEN      4
+#define SYS_ACCEPT      5
+#define SYS_GETSOCKNAME 6
+#define SYS_GETPEERNAME 7
+#define SYS_SOCKETPAIR  8
+#define SYS_SEND        9
+#define SYS_RECV        10
+#define SYS_SENDTO      11
+#define SYS_RECVFROM    12
+#define SYS_SHUTDOWN    13
+#define SYS_SETSOCKOPT  14
+#define SYS_GETSOCKOPT  15
+#define SYS_SENDMSG     16
+#define SYS_RECVMSG     17
+
+#define FIOSETOWN       0x8901
+#define SIOCSPGRP       0x8902
+#define FIOGETOWN       0x8903
+#define SIOCGPGRP       0x8904
+#define SIOCATMARK      0x8905
+#define SIOCGSTAMP      0x8906
diff --git a/src/network/socketpair.c b/src/network/socketpair.c
new file mode 100644
index 00000000..65a47fd9
--- /dev/null
+++ b/src/network/socketpair.c
@@ -0,0 +1,9 @@
+#include <sys/socket.h>
+#include "syscall.h"
+#include "socketcall.h"
+
+int socketpair(int domain, int type, int protocol, int fd[2])
+{
+	unsigned long args[] = { domain, type, protocol, (unsigned long)fd };
+	return syscall2(__NR_socketcall, SYS_SOCKETPAIR, (long)args);
+}