Implement Socket::getaddrinfo() and Socket::getnameinfo(), with related constants
authorPaul "LeoNerd" Evans <leonerd@leonerd.org.uk>
Mon, 13 Dec 2010 17:50:06 +0000 (17:50 +0000)
committerJesse Vincent <jesse@bestpractical.com>
Mon, 3 Jan 2011 04:21:34 +0000 (12:21 +0800)
ext/Socket/Makefile.PL
ext/Socket/Socket.pm
ext/Socket/Socket.xs

index 62ecab2..f22a22b 100644 (file)
@@ -14,12 +14,16 @@ my @names = (qw(AF_802 AF_AAL AF_APPLETALK AF_CCITT AF_CHAOS AF_CTF
                AF_LAST AF_LAT AF_LINK AF_MAX AF_NBS AF_NIT AF_NS
                AF_OSI AF_OSINET AF_PUP AF_ROUTE AF_SNA
                AF_UNIX AF_UNSPEC AF_USER AF_WAN AF_X25
+               AI_CANONNAME AI_NUMERICHOST AI_NUMERICSERV AI_PASSIVE
+               EAI_ADDRFAMILY EAI_AGAIN EAI_BADFLAGS EAI_FAIL EAI_FAMILY
+               EAI_NODATA EAI_NONAME EAI_SERVICE EAI_SOCKTYPE
                IOV_MAX IP_OPTIONS IP_HDRINCL IP_TOS IP_TTL IP_RECVOPTS
                IP_RECVRETOPTS IP_RETOPTS
                MSG_BCAST MSG_BTAG MSG_CTLFLAGS MSG_CTLIGNORE MSG_DONTWAIT
                MSG_EOF MSG_EOR MSG_ERRQUEUE MSG_ETAG MSG_FIN
                MSG_MAXIOVLEN MSG_MCAST MSG_NOSIGNAL MSG_RST MSG_SYN
                MSG_TRUNC MSG_URG MSG_WAITALL MSG_WIRE
+               NI_DGRAM NI_NAMEREQD NI_NUMERICHOST NI_NUMERICSERV
                PF_802 PF_AAL PF_APPLETALK PF_CCITT PF_CHAOS PF_CTF
                PF_DATAKIT PF_DECnet PF_DLI PF_ECMA PF_GOSIP PF_HYLINK
                PF_IMPLINK PF_INET PF_INET6 PF_ISO PF_KEY
index 2d6e4d5..a734ea6 100644 (file)
@@ -401,6 +401,24 @@ require XSLoader;
               inet_pton
               inet_ntop
 
+              getaddrinfo
+              getnameinfo
+
+              AI_CANONNAME
+              AI_NUMERICHOST
+              AI_NUMERICSERV
+              AI_PASSIVE
+
+              EAI_ADDRFAMILY
+              EAI_AGAIN
+              EAI_BADFLAGS
+              EAI_FAIL
+              EAI_FAMILY
+              EAI_NODATA
+              EAI_NONAME
+              EAI_SERVICE
+              EAI_SOCKTYPE
+
               IPPROTO_IP
               IPPROTO_IPV6
               IPPROTO_RAW
@@ -408,6 +426,11 @@ require XSLoader;
               IPPROTO_TCP
               IPPROTO_UDP
 
+              NI_DGRAM
+              NI_NAMEREQD
+              NI_NUMERICHOST
+              NI_NUMERICSERV
+
               TCP_KEEPALIVE
               TCP_MAXRT
               TCP_MAXSEG
index ff1fa7a..387965e 100644 (file)
@@ -220,10 +220,199 @@ not_here(const char *s)
 
 #include "const-c.inc"
 
+#ifdef HAS_GETADDRINFO
+static SV *err_to_SV(pTHX_ int err)
+{
+  SV *ret = sv_newmortal();
+  SvUPGRADE(ret, SVt_PVNV);
+
+  if(err) {
+    const char *error = gai_strerror(err);
+    sv_setpv(ret, error);
+  }
+  else {
+    sv_setpv(ret, "");
+  }
+
+  SvIV_set(ret, err); SvIOK_on(ret);
+
+  return ret;
+}
+
+static void xs_getaddrinfo(pTHX_ CV *cv)
+{
+    dVAR;
+    dXSARGS;
+
+    SV   *host;
+    SV   *service;
+    SV   *hints;
+
+    char *hostname = NULL;
+    char *servicename = NULL;
+    STRLEN len;
+    struct addrinfo hints_s = {};
+    struct addrinfo *res;
+    struct addrinfo *res_iter;
+    int err;
+    int n_res;
+
+    if(items > 3)
+      croak_xs_usage(cv, "host, service, hints");
+
+    SP -= items;
+
+    if(items < 1)
+      host = &PL_sv_undef;
+    else
+      host = ST(0);
+
+    if(items < 2)
+      service = &PL_sv_undef;
+    else
+      service = ST(1);
+
+    if(items < 3)
+      hints = NULL;
+    else
+      hints = ST(2);
+
+    SvGETMAGIC(host);
+    if(SvOK(host)) {
+      hostname = SvPV_nomg(host, len);
+      if (!len)
+        hostname = NULL;
+    }
+
+    SvGETMAGIC(service);
+    if(SvOK(service)) {
+      servicename = SvPV_nomg(service, len);
+      if (!len)
+        servicename = NULL;
+    }
+
+    if(hints && SvOK(hints)) {
+      HV *hintshash;
+      SV **valp;
+
+      if(!SvROK(hints) || SvTYPE(SvRV(hints)) != SVt_PVHV)
+        croak("hints is not a HASH reference");
+
+      hintshash = (HV*)SvRV(hints);
+
+      if((valp = hv_fetch(hintshash, "flags", 5, 0)) != NULL)
+        hints_s.ai_flags = SvIV(*valp);
+      if((valp = hv_fetch(hintshash, "family", 6, 0)) != NULL)
+        hints_s.ai_family = SvIV(*valp);
+      if((valp = hv_fetch(hintshash, "socktype", 8, 0)) != NULL)
+        hints_s.ai_socktype = SvIV(*valp);
+      if((valp = hv_fetch(hintshash, "protocol", 8, 0)) != NULL)
+        hints_s.ai_protocol = SvIV(*valp);
+    }
+
+    err = getaddrinfo(hostname, servicename, &hints_s, &res);
+
+    XPUSHs(err_to_SV(aTHX_ err));
+
+    if(err)
+      XSRETURN(1);
+
+    n_res = 0;
+    for(res_iter = res; res_iter; res_iter = res_iter->ai_next) {
+      HV *res_hv = newHV();
+
+      (void)hv_stores(res_hv, "family",   newSViv(res_iter->ai_family));
+      (void)hv_stores(res_hv, "socktype", newSViv(res_iter->ai_socktype));
+      (void)hv_stores(res_hv, "protocol", newSViv(res_iter->ai_protocol));
+
+      (void)hv_stores(res_hv, "addr",     newSVpvn((char*)res_iter->ai_addr, res_iter->ai_addrlen));
+
+      if(res_iter->ai_canonname)
+        (void)hv_stores(res_hv, "canonname", newSVpv(res_iter->ai_canonname, 0));
+      else
+        (void)hv_stores(res_hv, "canonname", newSV(0));
+
+      XPUSHs(sv_2mortal(newRV_noinc((SV*)res_hv)));
+      n_res++;
+    }
+
+    freeaddrinfo(res);
+
+    XSRETURN(1 + n_res);
+}
+#endif
+
+#ifdef HAS_GETNAMEINFO
+static void xs_getnameinfo(pTHX_ CV *cv)
+{
+    dVAR;
+    dXSARGS;
+
+    SV  *addr;
+    int  flags;
+
+    char host[1024];
+    char serv[256];
+    char *sa; /* we'll cast to struct sockaddr * when necessary */
+    STRLEN addr_len;
+    int err;
+
+    if(items < 1 || items > 2)
+      croak_xs_usage(cv, "addr, flags=0");
+
+    SP -= items;
+
+    addr = ST(0);
+
+    if(items < 2)
+      flags = 0;
+    else
+      flags = SvIV(ST(1));
+
+    if(!SvPOK(addr))
+      croak("addr is not a string");
+
+    addr_len = SvCUR(addr);
+
+    /* We need to ensure the sockaddr is aligned, because a random SvPV might
+     * not be due to SvOOK */
+    Newx(sa, addr_len, char);
+    Copy(SvPV_nolen(addr), sa, addr_len, char);
+#ifdef HAS_SOCKADDR_SA_LEN
+    ((struct sockaddr *)sa)->sa_len = addr_len;
+#endif
+
+    err = getnameinfo((struct sockaddr *)sa, addr_len,
+      host, sizeof(host),
+      serv, sizeof(serv),
+      flags);
+
+    Safefree(sa);
+
+    XPUSHs(err_to_SV(aTHX_ err));
+
+    if(err)
+      XSRETURN(1);
+
+    XPUSHs(sv_2mortal(newSVpv(host, 0)));
+    XPUSHs(sv_2mortal(newSVpv(serv, 0)));
+
+    XSRETURN(3);
+}
+#endif
+
 MODULE = Socket                PACKAGE = Socket
 
 INCLUDE: const-xs.inc
 
+BOOT:
+#ifdef HAS_GETADDRINFO
+  newXS("Socket::getaddrinfo", xs_getaddrinfo, __FILE__);
+#endif
+#ifdef HAS_GETNAMEINFO
+  newXS("Socket::getnameinfo", xs_getnameinfo, __FILE__);
+#endif
+
 void
 inet_aton(host)
        char *  host