Complete <netdb.h>.

Add all the missing <netdb.h> functions.

Also fix getservbyport to handle a null protocol correctly.

Also fix getservbyname/getservbyport to not interfere with getservent.

Also fix endservent to reset getservent iteration.

Also reduce unnecessary differences from upstream NetBSD sethostent.c.

The servent implementation is still horrific, and we should
probably support protoent too so that debugging tools can use
getprotobyname/getprotobynumber.

Bug: N/A
Test: ran tests
Change-Id: I639108c46df0a768af297cf3bbce857cb1bef9d9
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));
+}