make getaddrinfo with AF_UNSPEC and null host return both IPv4 and v6
[musl] / src / network / getaddrinfo.c
index c0f135f..4c1fe27 100644 (file)
@@ -47,7 +47,7 @@ struct aibuf {
 /* 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 getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res)
 {
        int flags = hint ? hint->ai_flags : 0;
        int family = hint ? hint->ai_family : AF_UNSPEC;
@@ -58,10 +58,9 @@ int getaddrinfo(const char *host, const char *serv, const struct addrinfo *hint,
        union sa sa = {{0}};
        unsigned char reply[1024];
        int i, j;
-       //char hostbuf[256];
        char line[512];
        FILE *f, _f;
-       unsigned char _buf[64];
+       unsigned char _buf[1024];
        char *z;
        int result;
        int cnt;
@@ -75,38 +74,63 @@ int getaddrinfo(const char *host, const char *serv, const struct addrinfo *hint,
                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 (!*serv) return EAI_SERVICE;
+               port = strtoul(serv, &z, 10);
+               if (*z) {
+                       size_t servlen = strlen(serv);
+                       char *end = line;
+
                        if (flags & AI_NUMERICSERV) return EAI_SERVICE;
 
-                       //f = fopen("/etc/services", "rb");
-                       return EAI_SERVICE;
+                       f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf);
+                       if (!f) return EAI_SERVICE;
+                       while (fgets(line, sizeof line, f)) {
+                               if (strncmp(line, serv, servlen) || !isspace(line[servlen]))
+                                       continue;
+                               port = strtoul(line+servlen, &end, 10);
+                               if (strncmp(end, proto==IPPROTO_UDP ? "/udp" : "/tcp", 4))
+                                       continue;
+                               break;
+                       }
+                       __fclose_ca(f);
+                       if (feof(f)) return EAI_SERVICE;
                }
+               if (port > 65535) return EAI_SERVICE;
                port = htons(port);
        }
 
        if (!host) {
-               if (family == AF_UNSPEC) family = AF_INET;
-               buf = calloc(sizeof *buf, 1+EXTRA);
+               if (family == AF_UNSPEC) {
+                       cnt = 2; family = AF_INET;
+               } else {
+                       cnt = 1;
+               }
+               buf = calloc(sizeof *buf, cnt);
                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;
+               for (i=0; i<cnt; i++) {
+                       if (i) family = AF_INET6;
+                       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 = family==AF_INET6
+                               ? sizeof sa.sin6 : sizeof sa.sin;
+                       buf[i].ai.ai_family = family;
+                       buf[i].sa.sin.sin_family = family;
+                       buf[i].sa.sin.sin_port = port;
+                       if (i+1<cnt) buf[i].ai.ai_next = &buf[i+1].ai;
+                       if (!(flags & AI_PASSIVE)) {
+                               if (family == AF_INET) {
+                                       0[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=127;
+                                       3[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=1;
+                               } else buf[i].sa.sin6.sin6_addr.s6_addr[15] = 1;
+                       }
                }
                *res = &buf->ai;
                return 0;
        }
 
+       if (!*host) return EAI_NONAME;
+
        /* Try as a numeric address */
        if (__ipparse(&sa, family, host) >= 0) {
                buf = calloc(sizeof *buf, 1+EXTRA);
@@ -204,7 +228,7 @@ int getaddrinfo(const char *host, const char *serv, const struct addrinfo *hint,
                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)
+       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++) {