getAddressInfo:...
authorStefan Vogel <sv@exept.de>
Wed, 18 Aug 2010 20:33:20 +0200
changeset 13025 c16326b73bf0
parent 13024 089f3e80f4a3
child 13026 8e8e14c406cf
getAddressInfo:... - try UNLIMITEDSTACK
UnixOperatingSystem.st
--- a/UnixOperatingSystem.st	Wed Aug 18 10:44:40 2010 +0200
+++ b/UnixOperatingSystem.st	Wed Aug 18 20:33:20 2010 +0200
@@ -11252,7 +11252,7 @@
 
 !UnixOperatingSystem::SocketHandle class methodsFor:'queries'!
 
-getAddressInfo:hostName serviceName:serviceNameArg domain:domainArg type:typeArg protocol:protoArg flags:flags
+XXgetAddressInfo:hostName serviceName:serviceNameArg domain:domainArg type:typeArg protocol:protoArg flags:flags
     "answer an Array of socket addresses for serviceName on hostName
      Domain, type, protocol may be nil or specify a hint for the socket
      addresses to be returned."
@@ -11626,6 +11626,76 @@
     "
 !
 
+getAddressInfo:hostName serviceName:serviceNameArg domain:domainArg type:typeArg protocol:protoArg flags:flags
+    "answer an Array of socket addresses for serviceName on hostName
+     Domain, type, protocol may be nil or specify a hint for the socket
+     addresses to be returned."
+
+    |result domain type proto serviceName|
+
+    domain := OperatingSystem domainCodeOf:domainArg.
+    type := OperatingSystem socketTypeCodeOf:typeArg.
+    proto := self protocolCodeOf:protoArg.
+    serviceNameArg notNil ifTrue:[
+        serviceName := serviceNameArg printString.      "convert integer port numbers"
+    ].
+
+    result := self primGetAddressInfo:hostName serviceName:serviceName domainCode:domain socketTypeCode:type protocolCode:proto flags:flags.
+    result isArray ifFalse:[
+        |request|
+        request := SocketAddressInfo new
+            domain:domainArg;
+            type:typeArg;
+            protocol:protoArg;
+            canonicalName:hostName;
+            serviceName:serviceName.
+        ^ (HostNameLookupError new
+                parameter:result;
+                messageText:' - ', (result printString);
+                request:request) raiseRequest.
+    ].
+    1 to:result size do:[:i |
+        |entry dom info|
+
+        entry := result at:i.
+
+        info := SocketAddressInfo new.
+        info
+            flags:(entry at:1);
+            domain:(dom := OperatingSystem domainSymbolOf:(entry at:2));
+            type:(OperatingSystem socketTypeSymbolOf:(entry at:3));
+            protocol:(self protocolSymbolOf:(entry at:4));
+            socketAddress:((SocketAddress newDomain:dom) fromBytes:(entry at:5));
+            canonicalName:(entry at:6).
+
+        result at:i put:info.
+    ].
+    ^ result
+
+    "
+     self getAddressInfo:'localhost' serviceName:nil
+            domain:nil type:nil protocol:nil flags:nil
+     self getAddressInfo:'localhost' serviceName:nil
+            domain:#inet type:#stream protocol:nil flags:nil
+     self getAddressInfo:'localhost' serviceName:nil
+            domain:#inet type:#stream protocol:#tcp flags:nil
+     self getAddressInfo:'blurb.exept.de' serviceName:nil
+            domain:#inet type:nil protocol:nil flags:nil
+     self getAddressInfo:'1.2.3.4' serviceName:'bla'
+            domain:#inet type:nil protocol:nil flags:nil
+     self getAddressInfo:'localhost' serviceName:'echo'
+            domain:#inet type:nil protocol:nil flags:nil
+     self getAddressInfo:nil serviceName:'echo'
+            domain:#inet type:nil protocol:nil flags:nil
+     self getAddressInfo:nil serviceName:nil
+            domain:#inet type:nil protocol:nil flags:nil
+     self getAddressInfo:'www.google.de' serviceName:nil
+            domain:nil type:nil protocol:nil flags:nil
+     self getAddressInfo:'smc1' serviceName:nil
+            domain:nil type:nil protocol:nil flags:nil
+    "
+!
+
 getNameInfo:socketAddress wantHostName:wantHostName wantServiceName:wantServiceName datagram:useDatagram flags:flags
     "answer an Array containing the hostName and serviceName
      in socketAddress.
@@ -11857,6 +11927,327 @@
                 domain:#inet type:#stream protocol:nil flags:nil) first socketAddress
          wantHostName:true wantServiceName:true datagram:false flags:0
     "
+!
+
+primGetAddressInfo:hostName serviceName:serviceName domainCode:domain socketTypeCode:type protocolCode:proto flags:flags
+    "answer an Array of socket addresses for serviceName on hostName
+     Domain, type, protocol may be nil or specify a hint for the socket
+     addresses to be returned."
+
+    |error errorString result|
+
+%{ /* UNLIMITEDSTACK */  /* Don't know whether DNS, NIS, LDAP or whatever is consulted */
+#undef xxAI_NUMERICHOST /* remove xx to test gethost...() path */
+
+
+#if !defined(NO_SOCKET)
+    char *__hostName, *__serviceName;
+    int ret, cnt = 0;
+
+    if (hostName == nil) {
+        __hostName = 0;
+    } else if (__isStringLike(hostName)) {
+        __hostName = __stringVal(hostName);
+    } else {
+        error = @symbol(badArgument1);
+        goto err;
+    }
+    if (serviceName == nil) {
+        __serviceName = 0;
+    } else if (__isStringLike(serviceName)) {
+        __serviceName = __stringVal(serviceName);
+    } else {
+        error = @symbol(badArgument2);
+        goto err;
+    }
+    if (__hostName == 0 && __serviceName == 0) {
+        error = @symbol(badArgument);
+        goto err;
+    }
+
+{
+# if defined(AI_NUMERICHOST)
+    /*
+     * Use getaddrinfo()
+     */
+    struct addrinfo hints;
+    struct addrinfo *info = NULL, *infop;
+
+    memset(&hints, 0, sizeof(hints));
+    if (__isSmallInteger(domain))
+        hints.ai_family = __intVal(domain);
+    if (__isSmallInteger(type))
+        hints.ai_socktype = __intVal(type);
+    if (__isSmallInteger(proto))
+        hints.ai_protocol = __intVal(proto);
+
+    do {
+        if (hostName == nil) {
+            __hostName = 0;
+        } else if (__isStringLike(hostName)) {
+            __hostName = __stringVal(hostName);
+        }
+        if (serviceName == nil) {
+            __serviceName = 0;
+        } else if (__isStringLike(serviceName)) {
+            __serviceName = __stringVal(serviceName);
+        }
+
+//        __BEGIN_INTERRUPTABLE__
+        ret = getaddrinfo(__hostName, __serviceName, &hints, &info);
+//        __END_INTERRUPTABLE__
+    } while (ret == EAI_SYSTEM && errno == EINTR);
+    if (ret != 0) {
+        switch (ret) {
+        case EAI_FAMILY:
+            error = @symbol(badProtocol);
+            break;
+        case EAI_SOCKTYPE:
+            error = @symbol(badSocketType);
+            break;
+        case EAI_BADFLAGS:
+            error = @symbol(badFlags);
+            break;
+        case EAI_NONAME:
+            error = @symbol(unknownHost);
+            break;
+        case EAI_SERVICE:
+            error = @symbol(unknownService);
+            break;
+#ifdef EAI_ADDRFAMILY
+        case EAI_ADDRFAMILY :
+            error = @symbol(unknownHostForProtocol);
+            break;
+#endif
+#ifdef EAI_NODATA
+        case EAI_NODATA:
+            error = @symbol(noAddress);
+            break;
+#endif
+        case EAI_MEMORY:
+            error = @symbol(allocationFailure);
+            break;
+        case EAI_FAIL:
+            error = @symbol(permanentFailure);
+            break;
+        case EAI_AGAIN:
+            error = @symbol(tryAgain);
+            break;
+        case EAI_SYSTEM:
+            error = @symbol(systemError);
+            break;
+        default:
+            error = @symbol(unknownError);
+        }
+        errorString = __MKSTRING(gai_strerror(ret));
+        goto err;
+    }
+    for (cnt=0, infop=info; infop; infop=infop->ai_next)
+        cnt++;
+
+    result = __ARRAY_NEW_INT(cnt);
+    if (result == nil) {
+        error = @symbol(allocationFailure);
+        goto err;
+    }
+    for (infop=info, cnt=0; infop; infop=infop->ai_next, cnt++) {
+        OBJ o, resp;
+
+        resp = __ARRAY_NEW_INT(6);
+        if (resp == nil) {
+            error = @symbol(allocationFailure);
+            goto err;
+        }
+
+        __ArrayInstPtr(result)->a_element[cnt] = resp; __STORE(result, resp);
+
+        __ArrayInstPtr(resp)->a_element[0] = __mkSmallInteger(infop->ai_flags);
+        __ArrayInstPtr(resp)->a_element[1] = __mkSmallInteger(infop->ai_family);
+        __ArrayInstPtr(resp)->a_element[2] = __mkSmallInteger(infop->ai_socktype);
+        __ArrayInstPtr(resp)->a_element[3] = __mkSmallInteger(infop->ai_protocol);
+
+        __PROTECT__(resp);
+        o = __BYTEARRAY_NEW_INT(infop->ai_addrlen);
+        __UNPROTECT__(resp);
+        if (o == nil) {
+            error = @symbol(allocationFailure);
+            goto err;
+        }
+        memcpy(__byteArrayVal(o), infop->ai_addr, infop->ai_addrlen);
+       __ArrayInstPtr(resp)->a_element[4] = o; __STORE(resp, o);
+
+        if (infop->ai_canonname) {
+            __PROTECT__(resp);
+            o = __MKSTRING(infop->ai_canonname);
+            __UNPROTECT__(resp);
+            if (o == nil) {
+                error = @symbol(allocationFailure);
+                goto err;
+            }
+            __ArrayInstPtr(resp)->a_element[5] = o; __STORE(resp, o);
+        }
+    }
+
+err:
+    if (info) freeaddrinfo(info);
+
+# else /* ! AI_NUMERICHOST =============================================================*/
+
+    /*
+     * Use getservbyname() / gethostByName()
+     */
+    struct hostent *hp;
+    char **addrpp;
+    int port = 0;
+    int i;
+
+    if (__serviceName) {
+        struct servent *sp;
+        char *__proto = 0;
+
+        if (__isStringLike(protoArg))
+            __proto = __stringVal(protoArg);
+
+        sp = getservbyname(__serviceName, __proto);
+        if (sp == NULL) {
+            errorString = @symbol(unknownService);
+            error = __mkSmallInteger(-3);
+            goto err;
+        }
+        port = sp->s_port;
+    }
+
+    if (__hostName) {
+#  ifdef USE_H_ERRNO
+        do {
+            if (hostName == nil) {
+                __hostName = 0;
+            } else if (__isStringLike(hostName)) {
+                __hostName = __stringVal(hostName);
+            }
+            /* __BEGIN_INTERRUPTABLE__ is dangerous, because gethostbyname
+             * uses a static data area
+             */
+            __BEGIN_INTERRUPTABLE__
+            hp = gethostbyname(__hostName);
+            __END_INTERRUPTABLE__
+        } while ((hp == NULL)
+                  && (
+                        (h_errno == TRY_AGAIN)
+                      || errno == EINTR
+#   ifdef IRIX5_3
+                      || (errno == ECONNREFUSED)
+#   endif
+                     )
+        );
+        if (hp == 0) {
+            switch (h_errno) {
+            case HOST_NOT_FOUND:
+                errorString = @symbol(unknownHost);
+                break;
+            case NO_ADDRESS:
+                errorString = @symbol(noAddress);
+                break;
+            case NO_RECOVERY:
+                errorString = @symbol(permanentFailure);
+                break;
+            case TRY_AGAIN:
+                errorString = @symbol(tryAgain);
+                break;
+            default:
+                errorString = @symbol(unknownError);
+                break;
+            }
+            error = __mkSmallInteger(h_errno);
+            goto err;
+        }
+#  else /* !USE_H_ERRNO */
+        hp = gethostbyname(__hostName);
+        if (hp == 0) {
+            errorString = @symbol(unknownHost);
+            error = __mkSmallInteger(-1);
+            goto err;
+        }
+#  endif /* !USE_H_ERRNO*/
+
+        if (__isSmallInteger(domain) && hp->h_addrtype != __smallIntegerVal(domain)) {
+            errorString = @symbol(unknownHost);
+            error = __mkSmallInteger(-2);
+            goto err;
+        }
+
+        for (cnt = 0, addrpp = hp->h_addr_list; *addrpp; addrpp++)
+            cnt++;
+        addrpp = hp->h_addr_list;
+    } else {
+        cnt = 1;
+    }
+
+    result = __ARRAY_NEW_INT(cnt);
+    if (result == nil) {
+        error = @symbol(allocationFailure);
+        goto err;
+    }
+
+    for (i = 0; i < cnt; i++) {
+        OBJ o, resp;
+        struct sockaddr_in *sa;
+
+        resp = __ARRAY_NEW_INT(6);
+        if (resp == nil) {
+            error = @symbol(allocationFailure);
+            goto err;
+        }
+
+        __ArrayInstPtr(result)->a_element[i] = resp; __STORE(result, resp);
+        __ArrayInstPtr(resp)->a_element[0] = __mkSmallInteger(0);
+        __ArrayInstPtr(resp)->a_element[2] = type; __STORE(result, type);
+        __ArrayInstPtr(resp)->a_element[3] = proto; __STORE(result, proto);
+        __PROTECT__(resp);
+        o = __BYTEARRAY_NEW_INT(sizeof(*sa));
+        __UNPROTECT__(resp);
+        if (o == nil) {
+            error = @symbol(allocationFailure);
+            goto err;
+        }
+        __ArrayInstPtr(resp)->a_element[4] = o; __STORE(resp, o);
+        sa = (struct sockaddr_in *)__byteArrayVal(o);
+        sa->sin_port = port;
+
+        if (__hostName) {
+            sa->sin_family = hp->h_addrtype;
+            memcpy(&sa->sin_addr, *addrpp, hp->h_length);
+            __ArrayInstPtr(resp)->a_element[1] = __mkSmallInteger(hp->h_addrtype);
+            if (hp->h_name) {
+                __PROTECT__(resp);
+                o = __MKSTRING(hp->h_name);
+                __UNPROTECT__(resp);
+                if (o == nil) {
+                    error = @symbol(allocationFailure);
+                    goto err;
+                }
+                __ArrayInstPtr(resp)->a_element[5] = o; __STORE(resp, o);
+            }
+            addrpp++;
+        } else{
+            __ArrayInstPtr(resp)->a_element[1] = domain; __STORE(resp, domain);
+        }
+    }
+
+err:;
+# endif /* ! AI_NUMERICHOST */
+}
+#else /* ! HAS_SOCKET */
+    error = @symbol(notImplemented);
+#endif
+%}.
+    error notNil ifTrue:[
+        errorString notNil ifTrue:[
+            ^ errorString.
+        ].
+        ^ error.
+    ].
+    ^ result.
 ! !
 
 !UnixOperatingSystem::SocketHandle methodsFor:'accepting'!
@@ -12700,11 +13091,11 @@
 !UnixOperatingSystem class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.276 2010-07-27 09:08:07 stefan Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.277 2010-08-18 18:33:20 stefan Exp $'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.276 2010-07-27 09:08:07 stefan Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.277 2010-08-18 18:33:20 stefan Exp $'
 ! !
 
 UnixOperatingSystem initialize!