UnixOperatingSystem.st
changeset 7136 aedf0aa8273b
parent 7133 fc8f147d884a
child 7137 8cda0aa2910f
--- a/UnixOperatingSystem.st	Wed Mar 26 14:14:53 2003 +0100
+++ b/UnixOperatingSystem.st	Thu Mar 27 14:28:00 2003 +0100
@@ -8468,6 +8468,11 @@
 
 !UnixOperatingSystem class methodsFor:'socket creation'!
 
+socketAccessor
+
+    ^ SocketHandle
+!
+
 socketWithDomain:domainArg type:typeArg protocol:protocolArg 
     "set up socket with domain, type and protocol number.
      This is a low level entry; no binding, listening or connect
@@ -11173,23 +11178,23 @@
      int code = -1;
 
      if (__isSmallInteger(aSymbolOrInteger) || aSymbolOrInteger == nil) {
-	RETURN(aSymbolOrInteger);
+        RETURN(aSymbolOrInteger);
      }
 #ifdef SOCK_STREAM
      else if (aSymbolOrInteger == @symbol(stream)) 
-	code = SOCK_STREAM;
+        code = SOCK_STREAM;
 #endif
 #ifdef SOCK_DGRAM
-     if (aSymbolOrInteger == @symbol(datagram)) 
-	code = SOCK_DGRAM;
+     else if (aSymbolOrInteger == @symbol(datagram)) 
+        code = SOCK_DGRAM;
 #endif
 #ifdef SOCK_RAW
-     if (aSymbolOrInteger == @symbol(raw)) 
-	code = SOCK_RAW;
+     else if (aSymbolOrInteger == @symbol(raw)) 
+        code = SOCK_RAW;
 #endif
 
      if (code > 0)
-	 typeCode = __MKSMALLINT(code);
+         typeCode = __MKSMALLINT(code);
 %}.
 
     ^ typeCode.
@@ -11401,6 +11406,530 @@
     ProtocolCache := nil.
 ! !
 
+!UnixOperatingSystem::SocketHandle class methodsFor:'queries'!
+
+getAddressInfo:hostName serviceName:serviceName 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."
+
+    |error errorString result domain type proto|
+
+    domain := self domainCodeOf:domainArg.
+    type := self socketTypeCodeOf:typeArg.
+    proto := self protocolCodeOf:protoArg.
+
+%{ /* STACK:32000 */
+/* #undef NI_NUMERICHOST */  /* undef to test gethost...() path */
+
+
+#if !defined(NO_SOCKET) 
+    char *__hostName, *__serviceName;
+    int ret, cnt = 0;
+
+    if (hostName == nil) {
+        __hostName = 0;
+    } else if (__isString(hostName) || __isSymbol(hostName)) {
+        __hostName = __stringVal(hostName);
+    } else {
+        error = @symbol(badArgument1);
+        goto err;
+    }
+    if (serviceName == nil) {
+        __serviceName = 0;
+    } else if (__isString(serviceName) || __isSymbol(serviceName)) {
+        __serviceName = __stringVal(serviceName);
+    } else {
+        error = @symbol(badArgument2);
+        goto err;
+    }
+    if (__hostName == 0 && __serviceName == 0) {
+        error = @symbol(badArgument);
+        goto err;
+    }
+
+{
+# if defined(AI_NUMERICHOST)
+    struct addrinfo hints;
+    struct addrinfo *info = 0, *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 {
+        __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;
+        case EAI_ADDRFAMILY :
+            error = @symbol(unknownHostForProtocol);
+            break;
+        case EAI_NODATA:
+            error = @symbol(noAddress);
+            break;
+        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] = __MKSMALLINT(infop->ai_flags);
+        __ArrayInstPtr(resp)->a_element[1] = __MKSMALLINT(infop->ai_family);
+        __ArrayInstPtr(resp)->a_element[2] = __MKSMALLINT(infop->ai_socktype);
+        __ArrayInstPtr(resp)->a_element[3] = __MKSMALLINT(infop->ai_protocol);
+        o = __BYTEARRAY_NEW_INT(infop->ai_addrlen);        
+        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) {
+            o = __MKSTRING(infop->ai_canonname);
+            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 =============================================================*/
+
+    struct hostent *hp;
+    char **addrpp;
+    int port = 0;
+    int i;
+
+    /*  
+     * Use gethostByName()
+     */
+
+    if (__serviceName) {
+        struct servent *sp;
+        char *__proto = 0;
+
+        if (__isString(protoArg) || __isSymbol(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 {
+            /* __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:
+                error = @symbol(unknownHost);
+                break;
+            case NO_ADDRESS:
+                error = @symbol(noAddress);
+                break;
+            case NO_RECOVERY:
+                error = @symbol(permanentFailure);
+                break;
+            case TRY_AGAIN:
+                error = @symbol(tryAgain);
+                break;
+            default:
+                error = @symbol(unknownError);
+            }
+            errorString = __MKSTRING(hstrerror(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;
+        __ArrayInstPtr(resp)->a_element[3] = proto;
+        o = __BYTEARRAY_NEW_INT(sizeof(*sa));        
+        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) {
+                o = __MKSTRING(hp->h_name);
+                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;
+        }
+    }
+
+err:;
+# endif /* ! AI_NUMERICHOST */
+}
+#else /* ! HAS_SOCKET */
+    error = @symbol(notImplemented);
+#endif
+%}.
+    error notNil ifTrue:[
+        NameLookupError raiseWith:error errorString:errorString.
+    ].
+    1 to:result size do:[:i | 
+        |entry dom info|
+
+        info := SocketAddressInfo new.
+        entry := result at:i.
+        info flags:(entry at:1).
+        info domain:(dom := self domainSymbolOf:(entry at:2)).
+        info type:(self socketTypeSymbolOf:(entry at:3)).
+        info protocol:(self protocolSymbolOf:(entry at:4)).
+        info socketAddress:((SocketAddress newDomain:dom) fromBytes:(entry at:5)).
+        info 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
+    "
+!
+
+getNameInfo:socketAddress wantHostName:wantHostName wantServiceName:wantServiceName datagram:useDatagram flags:flags 
+    "answer an Array containing the hostName and serviceName
+     in socketAddress. SocketType may be one "
+
+    |error errorString hostName serviceName|
+
+%{  /* STACK:32000 */
+
+/* #undef NI_NUMERICHOST */  /* undef to test gethost...() path */
+
+#if !defined(NO_SOCKET)
+
+# ifndef NI_MAXHOST
+#  define NI_MAXHOST 256
+#  define NI_MAXSERV 64
+# endif
+
+    char host[NI_MAXHOST];
+    char service[NI_MAXSERV];
+    char *hp = 0, *sp = 0;
+    int hsz = 0, ssz = 0;
+    int ret, __flags;
+
+    if (wantHostName == true) {
+        hp = host;
+        hsz = sizeof(host);
+    }
+    if (wantServiceName == true) {
+        sp = service;
+        ssz = sizeof(service);
+    }
+    if (hp == 0 && sp == 0) {
+        error = @symbol(badArgument);
+        goto err;
+    }
+    if (!__isNonNilObject(socketAddress) ||
+        (__intVal(__ClassInstPtr(__qClass(socketAddress))->c_flags) & ARRAYMASK) != BYTEARRAY) {
+        DBGPRINTF(("SOCKET: bad socket address"));
+        error = @symbol(badArgument1);
+        goto err;
+    }
+    if (!__isSmallInteger(flags)) {
+        error = @symbol(badArgument5);
+        goto err;
+    }
+    __flags = __intVal(flags);
+
+#if defined(NI_NUMERICHOST)
+    if (useDatagram == true) {
+        __flags |= NI_DGRAM;
+    }
+    
+    {
+        __BEGIN_INTERRUPTABLE__
+        ret = getnameinfo((struct sockaddr *)__byteArrayVal(socketAddress), 
+                          __byteArraySize(socketAddress),   
+                          hp, hsz, sp, ssz, __flags);
+        __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;
+        case EAI_ADDRFAMILY :
+            error = @symbol(unknownHostForProtocol);
+            break;
+        case EAI_NODATA:
+            error = @symbol(noAddress);
+            break;
+        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;
+    } 
+# else /* ! NI_NUMERICHOST */
+    /*
+     * Do it using gethostbyaddr()
+     */
+
+    struct sockaddr_in *sa;
+    if (__byteArraySize(socketAddress) < sizeof(*sa)) {
+        error = @symbol(badArgument1);
+        goto err;
+    }
+    sa = (struct sockaddr_in *)__byteArrayVal(socketAddress);
+    
+    if (sp) {
+        struct servent *servp;
+        char *__proto = 0;
+
+        __proto = (useDatagram == true ? "udp" : "tcp");
+
+        servp = getservbyport(sa->sin_port, __proto);
+        if (servp) {
+            sp = servp->s_name;
+        }
+    }
+    if (hp) {
+        struct hostent *hostp;
+#  ifdef USE_H_ERRNO
+        do {
+            /* __BEGIN_INTERRUPTABLE__ is dangerous, because gethostbyname
+             * uses a static data area
+             */
+            hostp = gethostbyaddr((char *)&sa->sin_addr, sizeof(sa->sin_addr), AF_INET);
+        } 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:
+                error = @symbol(unknownHost);
+                break;
+            case NO_ADDRESS:
+                error = @symbol(noAddress);
+                break;
+            case NO_RECOVERY:
+                error = @symbol(permanentFailure);
+                break;
+            case TRY_AGAIN:
+                error = @symbol(tryAgain);
+                break;
+            default:
+                error = @symbol(unknownError);
+            }
+            errorString = __MKSTRING(hstrerror(h_errno));
+            goto err;
+        } 
+#  else /* !USE_H_ERRNO */
+        hostp = gethostbyaddr(sa->sin_addr, sizeof(sa->sin_addr), AF_INET);
+        if (hostp == 0) {
+            errorString = @symbol(unknownHost);
+            error = __mkSmallInteger(-1);
+            goto err;
+        }
+#  endif /* !USE_H_ERRNO*/
+        hp = hostp->h_name;
+    }
+# endif /* ! NI_NUMERICHOST */
+
+    if (hp)
+        hostName = __MKSTRING(hp);        
+    if (sp)
+        serviceName = __MKSTRING(sp);
+
+err:;
+#else
+    error = @symbol(notImplemented);
+#endif
+%}.
+    error notNil ifTrue:[
+        NameLookupError raiseWith:error errorString:errorString.
+        ^ nil
+    ].
+
+    ^ Array with:hostName with:serviceName
+
+    "
+     self getNameInfo:
+        (self getAddressInfo:'localhost' serviceName:'echo' 
+                domain:#inet type:#stream protocol:nil flags:nil) first socketAddress
+         wantHostName:true wantServiceName:true datagram:false flags:0
+    "
+! !
+
 !UnixOperatingSystem::SocketHandle methodsFor:'accepting'!
 
 acceptWithPeerAddressBuffer:peerOrNil
@@ -12238,7 +12767,7 @@
 !UnixOperatingSystem class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.148 2003-03-26 10:24:36 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.149 2003-03-27 13:28:00 stefan Exp $'
 ! !
 
 UnixOperatingSystem initialize!