Migrate system/extra getaddrinfo test, and fix a bug in getservbyname(3).

This change is to migrate the getaddrinfo tests defined in the old file
system/extras/tests/bionic/libc/common/test_getaddrinfo.c
to the new place bionic/tests/netdb_test.cpp.

The test here is more thorough, and catches a bug in getservbyname(3)
that was breaking getaddrinfo(3)'s ability to look up services by name
without a hint that would cause it to ask for a specific protocol.

Change-Id: Ief5ebd0869496d1bc6a97861dfefa04bdf24bab1
Signed-off-by: Yongqin Liu <yongqin.liu@linaro.org>
diff --git a/libc/dns/net/getservbyname.c b/libc/dns/net/getservbyname.c
index c95c9b0..c32416c 100644
--- a/libc/dns/net/getservbyname.c
+++ b/libc/dns/net/getservbyname.c
@@ -25,29 +25,19 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include <sys/cdefs.h>
-#include <sys/types.h>
+
 #include <netdb.h>
+
 #include "servent.h"
 
-struct servent *
-getservbyname(const char *name, const char *proto)
-{
-    res_static       rs = __res_get_static();
-
-    if (rs == NULL || proto == NULL || name == NULL) {
-        errno = EINVAL;
-        return NULL;
+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;
     }
-
-    rs->servent_ptr = NULL;
-    while (1) {
-        struct servent*  s = getservent_r(rs);
-        if (s == NULL)
-            break;
-        if ( !strcmp( s->s_name, name ) && !strcmp( s->s_proto, proto ) )
-            return s;
-    }
-
-    return NULL;
+  }
+  return NULL;
 }
diff --git a/tests/netdb_test.cpp b/tests/netdb_test.cpp
index cc08715..ef2c8da 100644
--- a/tests/netdb_test.cpp
+++ b/tests/netdb_test.cpp
@@ -24,6 +24,47 @@
 TEST(netdb, getaddrinfo_NULL_hints) {
   addrinfo* ai = NULL;
   ASSERT_EQ(0, getaddrinfo("localhost", "9999", NULL, &ai));
+
+  bool saw_tcp = false;
+  bool saw_udp = false;
+  for (addrinfo* p = ai; p != NULL; p = p->ai_next) {
+    ASSERT_TRUE(p->ai_family == AF_INET || p->ai_family == AF_INET6);
+    if (p->ai_socktype == SOCK_STREAM) {
+      ASSERT_EQ(IPPROTO_TCP, p->ai_protocol);
+      saw_tcp = true;
+    } else if (p->ai_socktype == SOCK_DGRAM) {
+      ASSERT_EQ(IPPROTO_UDP, p->ai_protocol);
+      saw_udp = true;
+    }
+  }
+  ASSERT_TRUE(saw_tcp);
+  ASSERT_TRUE(saw_udp);
+
+  freeaddrinfo(ai);
+}
+
+TEST(netdb, getaddrinfo_service_lookup) {
+  addrinfo* ai = NULL;
+  ASSERT_EQ(0, getaddrinfo("localhost", "smtp", NULL, &ai));
+  ASSERT_EQ(SOCK_STREAM, ai->ai_socktype);
+  ASSERT_EQ(IPPROTO_TCP, ai->ai_protocol);
+  ASSERT_EQ(25, ntohs(reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_port));
+  freeaddrinfo(ai);
+}
+
+TEST(netdb, getaddrinfo_hints) {
+  addrinfo hints;
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = IPPROTO_TCP;
+
+  addrinfo* ai = NULL;
+  ASSERT_EQ(0, getaddrinfo( "localhost", "9999", &hints, &ai));
+  ASSERT_EQ(AF_INET, ai->ai_family);
+  ASSERT_EQ(SOCK_STREAM, ai->ai_socktype);
+  ASSERT_EQ(IPPROTO_TCP, ai->ai_protocol);
+  ASSERT_TRUE(ai->ai_next == NULL);
   freeaddrinfo(ai);
 }
 
@@ -65,3 +106,27 @@
   ASSERT_EQ(hent->h_addr[2], 0);
   ASSERT_EQ(hent->h_addr[3], 1);
 }
+
+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);
+  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_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);
+
+  // But there are actually udp services.
+  s = getservbyname("echo", "udp");
+  ASSERT_TRUE(s != NULL);
+  ASSERT_EQ(7, ntohs(s->s_port));
+  ASSERT_STREQ("udp", s->s_proto);
+}