Merge "Complete <netdb.h>."
diff --git a/docs/status.md b/docs/status.md
index 54d4c92..e6fb7a4 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -8,6 +8,7 @@
 
 New libc functions in P:
   * `__freading`/`__fwriting` (completing <stdio_ext.h>)
+  * `endhostent`/endnetent`/`endprotoent`/`getnetent`/`getprotoent`/`sethostent`/`setnetent`/`setprotoent` (completing <netdb.h>)
   * `getentropy`/`getrandom` (adding <sys/random.h>)
   * `getlogin_r`
   * `glob`/`globfree` (adding <glob.h>)
@@ -69,15 +70,10 @@
 aio_return
 aio_suspend
 aio_write
-endhostent
-endnetent
-endprotoent
 fexecve
 fmtmsg
 getdate
 getdate_err
-getnetent
-getprotoent
 lio_listio
 pthread_attr_getinheritsched
 pthread_attr_setinheritsched
@@ -95,9 +91,6 @@
 pthread_setcanceltype
 pthread_setschedprio
 pthread_testcancel
-sethostent
-setnetent
-setprotoent
 sockatmark
 swab
 wordexp
diff --git a/libc/NOTICE b/libc/NOTICE
index 9b461ee..c0e6265 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -4014,42 +4014,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 2004 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Christos Zoulas.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. All advertising materials mentioning features or use of this software
-   must display the following acknowledgement:
-       This product includes software developed by the NetBSD
-       Foundation, Inc. and its contributors.
-4. Neither the name of The NetBSD Foundation 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 BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
 Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
 Copyright (c) 1995,1999 by Internet Software Consortium.
 
diff --git a/libc/bionic/netdb.cpp b/libc/bionic/netdb.cpp
index da61f98..dc2a037 100644
--- a/libc/bionic/netdb.cpp
+++ b/libc/bionic/netdb.cpp
@@ -28,22 +28,40 @@
 
 #include <netdb.h>
 
-// We don't have an /etc/networks, so all inputs return NULL.
-netent* getnetbyname(const char* /*name*/) {
-  return NULL;
+// We don't have an /etc/networks or /etc/protocols, so these are just dummies.
+
+void endnetent() {
 }
 
-// We don't have an /etc/networks, so all inputs return NULL.
+void endprotoent() {
+}
+
 netent* getnetbyaddr(uint32_t /*net*/, int /*type*/) {
-  return NULL;
+  return nullptr;
 }
 
-// We don't have an /etc/protocols, so all inputs return NULL.
+netent* getnetbyname(const char* /*name*/) {
+  return nullptr;
+}
+
+netent* getnetent() {
+  return nullptr;
+}
+
 protoent* getprotobyname(const char* /*name*/) {
-  return NULL;
+  return nullptr;
 }
 
-// We don't have an /etc/protocols, so all inputs return NULL.
 protoent* getprotobynumber(int /*proto*/) {
-  return NULL;
+  return nullptr;
+}
+
+protoent* getprotoent() {
+  return nullptr;
+}
+
+void setnetent(int /*stayopen*/) {
+}
+
+void setprotoent(int /*stayopen*/) {
 }
diff --git a/libc/dns/net/getservbyname.c b/libc/dns/net/getservbyname.c
deleted file mode 100644
index c32416c..0000000
--- a/libc/dns/net/getservbyname.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <netdb.h>
-
-#include "servent.h"
-
-struct servent* getservbyname(const char* name, const char* proto) {
-  res_static rs = __res_get_static();
-  rs->servent_ptr = NULL;
-  struct servent* s;
-  while ((s = getservent_r(rs)) != NULL) {
-    if (strcmp(s->s_name, name) == 0 && (proto == NULL || strcmp(s->s_proto, proto) == 0)) {
-      return s;
-    }
-  }
-  return NULL;
-}
diff --git a/libc/dns/net/getservbyport.c b/libc/dns/net/getservbyport.c
deleted file mode 100644
index 7dcaafb..0000000
--- a/libc/dns/net/getservbyport.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <sys/cdefs.h>
-#include <netdb.h>
-#include "servent.h"
-
-struct servent *
-getservbyport(int port, const char *proto)
-{
-    res_static       rs = __res_get_static();
-
-    if (rs == NULL || proto == NULL) {
-        errno = EINVAL;
-        return NULL;
-    }
-
-    rs->servent_ptr = NULL;
-    while (1) {
-        struct servent*  s = getservent_r(rs);
-        if (s == NULL)
-            break;
-        if ( s->s_port == port && !strcmp( s->s_proto, proto ) )
-            return s;
-    }
-
-    return NULL;
-}
diff --git a/libc/dns/net/getservent.c b/libc/dns/net/getservent.c
index 0a727c7..03add59 100644
--- a/libc/dns/net/getservent.c
+++ b/libc/dns/net/getservent.c
@@ -25,32 +25,17 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include <sys/cdefs.h>
-#include <sys/types.h>
-#include <endian.h>
-#include <malloc.h>
+
 #include <netdb.h>
-#include "servent.h"
+
+#include <endian.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "resolv_static.h"
 #include "services.h"
 
-void
-setservent(int f)
-{
-    res_static  rs = __res_get_static();
-    if (rs) {
-        rs->servent_ptr = NULL;
-    }
-}
-
-void
-endservent(void)
-{
-    /* nothing to do */
-}
-
-struct servent *
-getservent_r( res_static  rs )
-{
+struct servent* getservent_r(res_static rs) {
     const char*  p;
     const char*  q;
     int          namelen;
@@ -119,12 +104,48 @@
     return &rs->servent;
 }
 
-struct servent *
-getservent(void)
-{
-    res_static   rs = __res_get_static();
+void setservent(int stayopen) {
+  endservent();
+}
 
-    if (rs == NULL) return NULL;
+void endservent(void) {
+  res_static rs = __res_get_static();
+  if (rs) rs->servent_ptr = NULL;
+}
 
-    return getservent_r(rs);
+struct servent* getservent(void) {
+  res_static rs = __res_get_static();
+  return rs ? getservent_r(rs) : NULL;
+}
+
+struct servent* getservbyname(const char* name, const char* proto) {
+  res_static rs = __res_get_static();
+  if (rs == NULL) return NULL;
+
+  const char* old_servent_ptr = rs->servent_ptr;
+  rs->servent_ptr = NULL;
+  struct servent* s;
+  while ((s = getservent_r(rs)) != NULL) {
+    if (strcmp(s->s_name, name) == 0 && (proto == NULL || strcmp(s->s_proto, proto) == 0)) {
+      break;
+    }
+  }
+  rs->servent_ptr = old_servent_ptr;
+  return s;
+}
+
+struct servent* getservbyport(int port, const char* proto) {
+  res_static rs = __res_get_static();
+  if (rs == NULL) return NULL;
+
+  const char* old_servent_ptr = rs->servent_ptr;
+  rs->servent_ptr = NULL;
+  struct servent* s;
+  while ((s = getservent_r(rs)) != NULL) {
+    if (s->s_port == port && (proto == NULL || strcmp(s->s_proto, proto) == 0)) {
+      break;
+    }
+  }
+  rs->servent_ptr = old_servent_ptr;
+  return s;
 }
diff --git a/libc/dns/net/servent.h b/libc/dns/net/servent.h
deleted file mode 100644
index 822b375..0000000
--- a/libc/dns/net/servent.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*	$NetBSD: servent.h,v 1.1 2005/04/18 19:39:45 kleink Exp $	*/
-
-/*-
- * Copyright (c) 2004 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Christos Zoulas.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *        This product includes software developed by the NetBSD
- *        Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation 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 BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include "resolv_static.h"
-
-struct servent*  getservent_r(res_static rs);
diff --git a/libc/dns/net/sethostent.c b/libc/dns/net/sethostent.c
index 916421e..66c305f 100644
--- a/libc/dns/net/sethostent.c
+++ b/libc/dns/net/sethostent.c
@@ -64,13 +64,26 @@
 
 static struct hostent *_hf_gethtbyname2(const char *, int, struct getnamaddr *);
 
-static const char *_h_hosts = _PATH_HOSTS;
+void
+/*ARGSUSED*/
+sethostent(int stayopen)
+{
+	res_static rs = __res_get_static();
+	if (rs) sethostent_r(&rs->hostf);
+}
+
+void
+endhostent(void)
+{
+	res_static rs = __res_get_static();
+	if (rs) endhostent_r(&rs->hostf);
+}
 
 void
 sethostent_r(FILE **hf)
 {
 	if (!*hf)
-		*hf = fopen(_h_hosts, "re");
+		*hf = fopen(_PATH_HOSTS, "re");
 	else
 		rewind(*hf);
 }
@@ -116,15 +129,13 @@
 	hp = _hf_gethtbyname2(name, af, info);
 #endif
 	if (hp == NULL) {
-		if (*info->he == NETDB_INTERNAL && errno == ENOSPC) {
-			return NS_UNAVAIL;
-		}
+		*info->he = HOST_NOT_FOUND;
 		return NS_NOTFOUND;
 	}
 	return NS_SUCCESS;
 }
 
-static struct hostent *
+struct hostent *
 _hf_gethtbyname2(const char *name, int af, struct getnamaddr *info)
 {
 	struct hostent *hp, hent;
@@ -145,7 +156,6 @@
 	}
 
 	if ((ptr = buf = malloc(len = info->buflen)) == NULL) {
-		endhostent_r(&hf);
 		*info->he = NETDB_INTERNAL;
 		return NULL;
 	}
@@ -161,12 +171,8 @@
 
 		hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen,
 		    info->he);
-		if (hp == NULL) {
-			if (*info->he == NETDB_INTERNAL && errno == ENOSPC) {
-				goto nospc;
-			}
+		if (hp == NULL)
 			break;
-		}
 
 		if (strcasecmp(hp->h_name, name) != 0) {
 			char **cp;
@@ -230,7 +236,6 @@
 	free(buf);
 	return hp;
 nospc:
-	endhostent_r(&hf);
 	*info->he = NETDB_INTERNAL;
 	free(buf);
 	errno = ENOSPC;
@@ -265,9 +270,6 @@
 	endhostent_r(&hf);
 
 	if (hp == NULL) {
-		if (errno == ENOSPC) {
-			return NS_UNAVAIL;
-		}
 		*info->he = HOST_NOT_FOUND;
 		return NS_NOTFOUND;
 	}
diff --git a/libc/include/netdb.h b/libc/include/netdb.h
index 66ec8e3..4ee0ce2 100644
--- a/libc/include/netdb.h
+++ b/libc/include/netdb.h
@@ -196,31 +196,47 @@
 
 __BEGIN_DECLS
 
+int getaddrinfo(const char* __node, const char* __service, const struct addrinfo* __hints, struct addrinfo** __result);
+void freeaddrinfo(struct addrinfo* __ptr);
+
+/* Android ABI error: POSIX getnameinfo(3) uses socklen_t rather than size_t. */
+int getnameinfo(const struct sockaddr* __sa, socklen_t __sa_length, char* __host, size_t __host_length, char* __service, size_t __service_length, int __flags);
+const char* gai_strerror(int __error);
+
+/* These functions are obsolete. Use getaddrinfo/getnameinfo instead. */
 #define h_errno (*__get_h_errno())
 int* __get_h_errno(void);
-void endservent(void);
+void herror(const char* __s);
+const char* hstrerror(int __error);
 struct hostent* gethostbyaddr(const void* __addr, socklen_t __length, int __type);
 int gethostbyaddr_r(const void* __addr, socklen_t __length, int __type, struct hostent* __ret, char* __buf, size_t __buf_size, struct hostent** __result, int* __h_errno_ptr) __INTRODUCED_IN(23);
 struct hostent* gethostbyname(const char* __name);
 int gethostbyname_r(const char* __name, struct hostent* __ret, char* __buf, size_t __buf_size, struct hostent** __result, int* __h_errno_ptr);
 struct hostent* gethostbyname2(const char* __name, int __af);
 int gethostbyname2_r(const char* __name, int __af, struct hostent* __ret, char* __buf, size_t __buf_size, struct hostent** __result, int* __h_errno_ptr) __INTRODUCED_IN(23);
+void endhostent(void) __INTRODUCED_IN_FUTURE;
 struct hostent* gethostent(void);
+void sethostent(int __stay_open) __INTRODUCED_IN_FUTURE;
+
+/* These functions are obsolete. None of these functions return anything but nullptr. */
+void endnetent(void) __INTRODUCED_IN_FUTURE;
 struct netent* getnetbyaddr(uint32_t __net, int __type);
 struct netent* getnetbyname(const char* __name);
+struct netent* getnetent(void) __INTRODUCED_IN_FUTURE;
+void setnetent(int __stay_open) __INTRODUCED_IN_FUTURE;
+
+/* None of these functions return anything but nullptr. */
+void endprotoent(void) __INTRODUCED_IN_FUTURE;
 struct protoent* getprotobyname(const char* __name);
 struct protoent* getprotobynumber(int __proto);
-struct servent* getservbyname(const char* __name, const char* __proto);
-struct servent* getservbyport(int __port, const char* __proto);
-struct servent* getservent(void);
-void herror(const char* __s);
-const char* hstrerror(int __error);
+struct protoent* getprotoent(void) __INTRODUCED_IN_FUTURE;
+void setprotoent(int __stay_open) __INTRODUCED_IN_FUTURE;
 
-int getaddrinfo(const char* __node, const char* __service, const struct addrinfo* __hints, struct addrinfo** __result);
-/* POSIX getnameinfo uses socklen_t, not size_t, but LP64 sizeof(socklen_t) != sizeof(size_t). */
-int getnameinfo(const struct sockaddr* __sa, socklen_t __sa_length, char* __host, size_t __host_length, char* __service, size_t __service_length, int __flags);
-void freeaddrinfo(struct addrinfo* __ptr);
-const char* gai_strerror(int __error);
+/* These functions return entries from a built-in database. */
+void endservent(void);
+struct servent* getservbyname(const char* __name, const char* __proto);
+struct servent* getservbyport(int __port_in_network_order, const char* __proto);
+struct servent* getservent(void);
 void setservent(int __stay_open);
 
 __END_DECLS
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 08ba59f..82d6872 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1322,7 +1322,12 @@
   global:
     __freading;
     __fwriting;
+    endhostent;
+    endnetent;
+    endprotoent;
     getentropy;
+    getnetent;
+    getprotoent;
     getrandom;
     getlogin_r;
     glob;
@@ -1357,6 +1362,9 @@
     posix_spawn_file_actions_destroy;
     posix_spawn_file_actions_init;
     posix_spawnp;
+    sethostent;
+    setnetent;
+    setprotoent;
     syncfs;
 } LIBC_O;
 
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 400c95f..5dfe93b 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1242,7 +1242,12 @@
   global:
     __freading;
     __fwriting;
+    endhostent;
+    endnetent;
+    endprotoent;
     getentropy;
+    getnetent;
+    getprotoent;
     getrandom;
     getlogin_r;
     glob;
@@ -1277,6 +1282,9 @@
     posix_spawn_file_actions_destroy;
     posix_spawn_file_actions_init;
     posix_spawnp;
+    sethostent;
+    setnetent;
+    setprotoent;
     syncfs;
 } LIBC_O;
 
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index eb5c1e4..8f7b599 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1347,7 +1347,12 @@
   global:
     __freading;
     __fwriting;
+    endhostent;
+    endnetent;
+    endprotoent;
     getentropy;
+    getnetent;
+    getprotoent;
     getrandom;
     getlogin_r;
     glob;
@@ -1382,6 +1387,9 @@
     posix_spawn_file_actions_destroy;
     posix_spawn_file_actions_init;
     posix_spawnp;
+    sethostent;
+    setnetent;
+    setprotoent;
     syncfs;
 } LIBC_O;
 
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 16f1209..3a039c5 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1306,7 +1306,12 @@
   global:
     __freading;
     __fwriting;
+    endhostent;
+    endnetent;
+    endprotoent;
     getentropy;
+    getnetent;
+    getprotoent;
     getrandom;
     getlogin_r;
     glob;
@@ -1341,6 +1346,9 @@
     posix_spawn_file_actions_destroy;
     posix_spawn_file_actions_init;
     posix_spawnp;
+    sethostent;
+    setnetent;
+    setprotoent;
     syncfs;
 } LIBC_O;
 
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 400c95f..5dfe93b 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1242,7 +1242,12 @@
   global:
     __freading;
     __fwriting;
+    endhostent;
+    endnetent;
+    endprotoent;
     getentropy;
+    getnetent;
+    getprotoent;
     getrandom;
     getlogin_r;
     glob;
@@ -1277,6 +1282,9 @@
     posix_spawn_file_actions_destroy;
     posix_spawn_file_actions_init;
     posix_spawnp;
+    sethostent;
+    setnetent;
+    setprotoent;
     syncfs;
 } LIBC_O;
 
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 94ee319..2b165cc 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1304,7 +1304,12 @@
   global:
     __freading;
     __fwriting;
+    endhostent;
+    endnetent;
+    endprotoent;
     getentropy;
+    getnetent;
+    getprotoent;
     getrandom;
     getlogin_r;
     glob;
@@ -1339,6 +1344,9 @@
     posix_spawn_file_actions_destroy;
     posix_spawn_file_actions_init;
     posix_spawnp;
+    sethostent;
+    setnetent;
+    setprotoent;
     syncfs;
 } LIBC_O;
 
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 400c95f..5dfe93b 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1242,7 +1242,12 @@
   global:
     __freading;
     __fwriting;
+    endhostent;
+    endnetent;
+    endprotoent;
     getentropy;
+    getnetent;
+    getprotoent;
     getrandom;
     getlogin_r;
     glob;
@@ -1277,6 +1282,9 @@
     posix_spawn_file_actions_destroy;
     posix_spawn_file_actions_init;
     posix_spawnp;
+    sethostent;
+    setnetent;
+    setprotoent;
     syncfs;
 } LIBC_O;
 
diff --git a/tests/netdb_test.cpp b/tests/netdb_test.cpp
index a35f703..e699701 100644
--- a/tests/netdb_test.cpp
+++ b/tests/netdb_test.cpp
@@ -298,24 +298,152 @@
 
 TEST(netdb, getservbyname) {
   // smtp is TCP-only, so we know we'll get 25/tcp back.
-  servent* s = getservbyname("smtp", NULL);
-  ASSERT_TRUE(s != NULL);
+  servent* s = getservbyname("smtp", nullptr);
+  ASSERT_TRUE(s != nullptr);
+  ASSERT_STREQ("smtp", s->s_name);
   ASSERT_EQ(25, ntohs(s->s_port));
   ASSERT_STREQ("tcp", s->s_proto);
 
   // We get the same result by explicitly asking for tcp.
   s = getservbyname("smtp", "tcp");
-  ASSERT_TRUE(s != NULL);
+  ASSERT_TRUE(s != nullptr);
+  ASSERT_STREQ("smtp", s->s_name);
   ASSERT_EQ(25, ntohs(s->s_port));
   ASSERT_STREQ("tcp", s->s_proto);
 
   // And we get a failure if we explicitly ask for udp.
   s = getservbyname("smtp", "udp");
-  ASSERT_TRUE(s == NULL);
+  ASSERT_TRUE(s == nullptr);
 
   // But there are actually udp services.
   s = getservbyname("echo", "udp");
-  ASSERT_TRUE(s != NULL);
+  ASSERT_TRUE(s != nullptr);
+  ASSERT_STREQ("echo", s->s_name);
   ASSERT_EQ(7, ntohs(s->s_port));
   ASSERT_STREQ("udp", s->s_proto);
 }
+
+TEST(netdb, getservbyport) {
+  // smtp is TCP-only, so we know we'll get 25/tcp back.
+  servent* s = getservbyport(htons(25), nullptr);
+  ASSERT_TRUE(s != nullptr);
+  ASSERT_STREQ("smtp", s->s_name);
+  ASSERT_EQ(25, ntohs(s->s_port));
+  ASSERT_STREQ("tcp", s->s_proto);
+
+  // We get the same result by explicitly asking for tcp.
+  s = getservbyport(htons(25), "tcp");
+  ASSERT_TRUE(s != nullptr);
+  ASSERT_STREQ("smtp", s->s_name);
+  ASSERT_EQ(25, ntohs(s->s_port));
+  ASSERT_STREQ("tcp", s->s_proto);
+
+  // And we get a failure if we explicitly ask for udp.
+  s = getservbyport(htons(25), "udp");
+  ASSERT_TRUE(s == nullptr);
+
+  // But there are actually udp services.
+  s = getservbyport(htons(7), "udp");
+  ASSERT_TRUE(s != nullptr);
+  ASSERT_STREQ("echo", s->s_name);
+  ASSERT_EQ(7, ntohs(s->s_port));
+  ASSERT_STREQ("udp", s->s_proto);
+}
+
+TEST(netdb, endnetent_getnetent_setnetent) {
+  setnetent(0);
+  setnetent(1);
+  endnetent();
+  while (getnetent() != nullptr) {
+  }
+}
+
+TEST(netdb, getnetbyaddr) {
+  getnetbyaddr(0, 0);
+}
+
+TEST(netdb, getnetbyname) {
+  getnetbyname("x");
+}
+
+TEST(netdb, endprotoent_getprotoent_setprotoent) {
+  setprotoent(0);
+  setprotoent(1);
+  endprotoent();
+  while (getprotoent() != nullptr) {
+  }
+}
+
+TEST(netdb, getprotobyname) {
+  getprotobyname("tcp");
+}
+
+TEST(netdb, getprotobynumber) {
+  getprotobynumber(6);
+}
+
+TEST(netdb, endservent_getservent_setservent) {
+  setservent(0);
+  setservent(1);
+  endservent();
+  size_t service_count = 0;
+  while (getservent() != nullptr) {
+    ++service_count;
+  }
+  ASSERT_GT(service_count, 0U);
+}
+
+TEST(netdb, getservbyname_getservent_conflicts) {
+  // Calling getservbyname shouldn't affect getservent's iteration order.
+  endservent();
+  while (getservent() != nullptr) {
+    ASSERT_TRUE(getservbyname("smtp", "tcp") != nullptr);
+  }
+}
+
+TEST(netdb, getservbyport_getservent_conflicts) {
+  // Calling getservbyport shouldn't affect getservent's iteration order.
+  endservent();
+  while (getservent() != nullptr) {
+    ASSERT_TRUE(getservbyport(htons(25), "tcp") != nullptr);
+  }
+}
+
+TEST(netdb, endservent_resets) {
+  endservent();
+  std::string first_service(getservent()->s_name);
+  endservent();
+  ASSERT_EQ(first_service, std::string(getservent()->s_name));
+}
+
+TEST(netdb, setservent_resets) {
+  endservent();
+  std::string first_service(getservent()->s_name);
+  setservent(0);
+  ASSERT_EQ(first_service, std::string(getservent()->s_name));
+}
+
+TEST(netdb, endhostent_gethostent_sethostent) {
+  sethostent(0);
+  sethostent(1);
+  endhostent();
+  size_t host_count = 0;
+  while (gethostent() != nullptr) {
+    ++host_count;
+  }
+  ASSERT_GT(host_count, 0U);
+}
+
+TEST(netdb, endhostent_resets) {
+  endhostent();
+  std::string first_host(gethostent()->h_name);
+  endhostent();
+  ASSERT_EQ(first_host, std::string(gethostent()->h_name));
+}
+
+TEST(netdb, sethostent_resets) {
+  endhostent();
+  std::string first_host(gethostent()->h_name);
+  sethostent(0);
+  ASSERT_EQ(first_host, std::string(gethostent()->h_name));
+}