SocketAddress.st
author Claus Gittinger <cg@exept.de>
Sat, 02 May 2020 21:40:13 +0200
changeset 5476 7355a4b11cb6
parent 4670 a23c3ec06b51
permissions -rw-r--r--
#FEATURE by cg class: Socket class added: #newTCPclientToHost:port:domain:domainOrder:withTimeout: changed: #newTCPclientToHost:port:domain:withTimeout:

"{ Encoding: utf8 }"

"
 COPYRIGHT (c) 1995 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic2' }"

"{ NameSpace: Smalltalk }"

UninterpretedBytes variableByteSubclass:#SocketAddress
	instanceVariableNames:''
	classVariableNames:'DomainToClassMapping Verbose LastName LastPort LastType LastAddr
		LastTime'
	poolDictionaries:''
	category:'OS-Sockets'
!

!SocketAddress primitiveDefinitions!
%{
#include "stxOSDefs.h"
%}
! !

!SocketAddress class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1995 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    Abstract superclass for subclasses implementing various IPC addressing schemes.
    See concrete examples IPSocketAddress and UDSocketAddress.

    ST-80 compatibility class.
    This may be required when existing code has to be ported to ST/X;
    however, it may not be complete and more protocol may be added in the future.
    The code here was created when public domain code (Manchester) had to
    be ported to ST/X and missing classes/methods were encountered, and code added
    by reasoning 'what the original class could probably do there'.

    This is an additional goody class; therefore:

    THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTOR ``AS IS'' AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTOR BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.
"
! !

!SocketAddress class methodsFor:'instance creation'!

allForHostName:name
    "get a collection of new instances given a hostname"

    ^ self allForHostName:name serviceName:nil type:#stream.

    "Modified: / 21-02-2017 / 21:17:21 / stefan"
!

allForHostName:name port:portNr
    "get a collection of new instances given a hostname and port"

    ^ self allForHostName:name serviceName:portNr domain:nil type:nil

    "Modified: / 21-02-2017 / 21:40:19 / stefan"
!

allForHostName:name serviceName:portNrOrName domain:domainSymbol type:socketTypeSymbol
    "get a collection of new instances given a hostname, port or service and type.
     Multi-homed hosts return more than one entry"

    ^ (self 
        getAddressInfo:name 
        serviceName:portNrOrName 
        domain:domainSymbol 
        type:socketTypeSymbol
        protocol:nil 
        flags:0
    ) collect:[:eachAddressInfo| eachAddressInfo socketAddress].
        

    "
     SocketAddress allForHostName:'localhost' serviceName:10 type:#stream
     SocketAddress allForHostName:'localhost' serviceName:'10' type:#stream
     IPSocketAddress allForHostName:'localhost' serviceName:'echo' type:#datagram 
     IPSocketAddress allForHostName:'localhost' serviceName:'echo' type:nil
     SocketAddress allForHostName:'google.com' serviceName:'http' type:#stream
     IPSocketAddress allForHostName:'google.com' serviceName:'http' type:nil
    "

    "Created: / 21-02-2017 / 21:34:25 / stefan"
!

allForHostName:name serviceName:portNrOrName type:socketTypeSymbol
    "get a collection of new instances given a hostname, port or service and type.
     Multi-homed hosts return more than one entry"

    ^ self allForHostName:name serviceName:portNrOrName domain:self domain type:socketTypeSymbol

    "
     SocketAddress allForHostName:'localhost' serviceName:10 type:#stream
     SocketAddress allForHostName:'localhost' serviceName:'10' type:#stream
     IPSocketAddress allForHostName:'localhost' serviceName:'echo' type:#datagram 
     IPSocketAddress allForHostName:'localhost' serviceName:'echo' type:nil
     SocketAddress allForHostName:'google.com' serviceName:'http' type:#stream
     IPSocketAddress allForHostName:'google.com' serviceName:'http' type:nil
    "

    "Modified: / 21-02-2017 / 21:35:17 / stefan"
!

anyHost
    "get a new instance representing the ANY-host address"

    ^ self hostAddress:self anyAddress
!

fromBytes:bytesArg
    "create a socket address from bytes (ByteArray), 
     returned by the primitive syscalls.
     Raises an error, if the domainType is not supported by this Smalltalk
     (i.e. if libbasic was compiled without support for the corresponding AF_xxx support)"

    |domainNr domainSymbol bytes|

    bytes := bytesArg asByteArray.

    "/ cg: code used to be:
    "/      domainNr := bytes unsignedShortAt:1.
    "/ however, some systems use a different structure, which includes the struct length
    "/ in byte 1 (APPLE).
    "/ so better let the C-compiler figure out how things are layed out...
%{
    if (__isByteArrayLike(bytes)) {
        struct sockaddr *s = (struct sockaddr *)(__ByteArrayInstPtr(bytes)->ba_element);

        domainNr = __mkSmallInteger(s->sa_family);
    }
%}.
    domainSymbol := OperatingSystem domainSymbolOf:domainNr.
    domainSymbol isNil ifTrue:[
       self error:('unsupported domain (type=%1)' bindWith:domainNr)
    ].
    ^ (SocketAddress newDomain:domainSymbol) fromBytes:bytes.
!

hostAddress:addr
    "get a new instance given addr-bytes"

    ^ self new hostAddress:addr

    "
     IPSocketAddress hostAddress:#[127 0 0 1]
    "
!

hostAddress:addr port:portNr
    "get a new instance given addr-bytes and a portNr"

    ^ self new hostAddress:addr; port:portNr
!

hostName:name
    "get a new instance given a hostname"

    ^ self hostName:name serviceName:nil type:nil.

    "Modified: / 17-06-2009 / 15:25:42 / sr"
!

hostName:name port:portNr
    "get a new instance given a hostname and port"

    "Solaris 9 has a problem with some service names if type is missing!!"
    ^ self hostName:name serviceName:portNr type:#SOCK_STREAM
!

hostName:name serviceName:portNrOrName type:socketTypeSymbol
    "get a new instance given a hostname, port or service and type.
     Do not use this, since it does not work for multi-homed hosts"

    |addressList sa|

    LastName = name ifTrue:[
        LastPort = portNrOrName ifTrue:[
            LastType = socketTypeSymbol ifTrue:[
                (Timestamp now secondDeltaFrom: LastTime) <= 30 ifTrue:[
                    ^ LastAddr 
                ]
            ]
        ]
    ].

    addressList := self allForHostName:name serviceName:portNrOrName type:socketTypeSymbol.
    addressList isNil ifTrue:[^ nil].

    sa := addressList last.
    addressList size > 1 ifTrue:[
        (addressList contains:[:entry| entry  ~= sa]) ifTrue:[
            Verbose == true ifTrue:[
                ('SocketAddress [warning]: multiple hostAddresses for host: ', name) infoPrintCR.
                addressList do:[:entry| '  ' infoPrint. entry infoPrintCR].
                (addressList detect:[:entry| entry ~= sa] ifNone:nil) infoPrintCR.
            ].
        ].
    ].
    LastTime := Timestamp now.
    LastName := name.
    LastPort := portNrOrName.
    LastType := socketTypeSymbol.
    LastAddr := sa.
    ^ sa.

    "
     SocketAddress hostName:'localhost' serviceName:10 type:#stream  
     IPSocketAddress hostName:'localhost' serviceName:'echo' type:#datagram 
     IPSocketAddress hostName:'localhost' serviceName:'echo' type:nil
     IPSocketAddress hostName:'www.google.com' serviceName:'http' type:nil
     SocketAddress allForHostName:'localhost' serviceName:10 type:#stream.
     SocketAddress allForHostName:'www.google.com' serviceName:10 type:#stream.
    "

    "Modified: / 17-06-2009 / 15:25:35 / sr"
!

new
    |numBytes|

    self == SocketAddress ifTrue:[
        self abstractClassInstantiationError.
    ].

    numBytes := self socketAddressSize.
    numBytes isNil ifTrue:[
        self error:'unsupported domain'.
        ^ nil
    ].
    ^ (self new:numBytes) domainCode:(self domainCode)

    "
     IPSocketAddress new            
     IPv6SocketAddress new          
     UDSocketAddress new            
     AppletalkSocketAddress new
   "
!

newDomain:domain
   "answer an new socket address for a given domain"

   ^ (self knownClassFromCode:domain) new

   "
    self newDomain:#afUnix    
    self newDomain:#afInet    

    self newDomain:#'AF_UNIX' 
    self newDomain:#'AF_INET' 

    self newDomain:#unix
    self newDomain:#inet
   "
! !

!SocketAddress class methodsFor:'addressing'!

anyAddress
    self subclassResponsibility
! !

!SocketAddress class methodsFor:'queries'!

domain
    "Answer the domain symbol. Subclasses redefine this"

    self == SocketAddress ifTrue:[
        ^ nil
    ] ifFalse:[
        self subclassResponsibility
    ]
!

domainCode
    "answer the numerical domain code used in socket addresses"

    ^ OperatingSystem domainCodeOf:self domain
!

domainCodeFromName:aNameSymbol
    "this is a compatibility method;
     VW returns the internal unix codes here - however, in ST/X,
     symbols are returned, which are translated later"

    aNameSymbol isInteger ifTrue:[ ^ aNameSymbol ].

    "here, all possible variants are translated to AF_* symbols,
     which are handled by the primitives"

    aNameSymbol == #afUnix      ifTrue:[^ #'AF_UNIX'].
    aNameSymbol == #AF_UNIX     ifTrue:[^ #'AF_UNIX'].
    aNameSymbol == #unix        ifTrue:[^ #'AF_UNIX'].

    aNameSymbol == #afInet      ifTrue:[^ #'AF_INET'].    
    aNameSymbol == #'AF_INET'   ifTrue:[^ #'AF_INET'].    
    aNameSymbol == #inet        ifTrue:[^ #'AF_INET'].    

    aNameSymbol == #afIpV6      ifTrue:[^ #'AF_INET6'].    
    aNameSymbol == #'AF_INET6'  ifTrue:[^ #'AF_INET6'].    
    aNameSymbol == #inet6       ifTrue:[^ #'AF_INET6'].    
    aNameSymbol == #afInet6     ifTrue:[^ #'AF_INET6'].    

    aNameSymbol == #afAppletalk    ifTrue:[^ #'AF_APPLETALK'].    
    aNameSymbol == #'AF_APPLETALK' ifTrue:[^ #'AF_APPLETALK'].    
    aNameSymbol == #appletalk      ifTrue:[^ #'AF_APPLETALK'].    

    aNameSymbol == #afDecnet    ifTrue:[^ #'AF_DECnet'].    
    aNameSymbol == #'AF_DECnet' ifTrue:[^ #'AF_DECnet'].    
    aNameSymbol == #'AF_DECNET' ifTrue:[^ #'AF_DECnet'].    
    aNameSymbol == #decnet      ifTrue:[^ #'AF_DECnet'].    
    aNameSymbol == #decNet      ifTrue:[^ #'AF_DECnet'].    
    aNameSymbol == #DECnet      ifTrue:[^ #'AF_DECnet'].    

    aNameSymbol == #afSna       ifTrue:[^ #'AF_SNA'].    
    aNameSymbol == #'AF_SNA'    ifTrue:[^ #'AF_SNA'].    
    aNameSymbol == #sna         ifTrue:[^ #'AF_SNA'].    

    aNameSymbol == #afNs        ifTrue:[^ #'AF_NS'].    
    aNameSymbol == #'AF_NS'     ifTrue:[^ #'AF_NS'].    
    aNameSymbol == #ns          ifTrue:[^ #'AF_NS'].    
    aNameSymbol == #xns         ifTrue:[^ #'AF_NS'].    

    aNameSymbol == #afCcitt     ifTrue:[^ #'AF_CCITT'].    
    aNameSymbol == #'AF_CCITT'  ifTrue:[^ #'AF_CCITT'].    
    aNameSymbol == #ccitt       ifTrue:[^ #'AF_CCITT'].    

    aNameSymbol == #afImplink    ifTrue:[^ #'AF_IMPLINK'].    
    aNameSymbol == #'AF_IMPLINK' ifTrue:[^ #'AF_IMPLINK'].    
    aNameSymbol == #implink      ifTrue:[^ #'AF_IMPLINK'].    

    aNameSymbol == #afPup       ifTrue:[^ #'AF_PUP'].    
    aNameSymbol == #'AF_PUP'    ifTrue:[^ #'AF_PUP'].    
    aNameSymbol == #pup         ifTrue:[^ #'AF_PUP'].    

    aNameSymbol == #afChaos    ifTrue:[^ #'AF_CHAOS'].    
    aNameSymbol == #'AF_CHAOS' ifTrue:[^ #'AF_CHAOS'].    
    aNameSymbol == #chaos      ifTrue:[^ #'AF_CHAOS'].    

    aNameSymbol == #afEcma      ifTrue:[^ #'AF_ECMA'].    
    aNameSymbol == #'AF_ECMA'   ifTrue:[^ #'AF_ECMA'].    
    aNameSymbol == #ecma        ifTrue:[^ #'AF_ECMA'].    

    aNameSymbol == #afDatakit    ifTrue:[^ #'AF_DATAKIT'].    
    aNameSymbol == #'AF_DATAKIT' ifTrue:[^ #'AF_DATAKIT'].    
    aNameSymbol == #datakit      ifTrue:[^ #'AF_DATAKIT'].    

    aNameSymbol == #afDli     ifTrue:[^ #'AF_DLI'].    
    aNameSymbol == #'AF_DLI'  ifTrue:[^ #'AF_DLI'].    
    aNameSymbol == #dli       ifTrue:[^ #'AF_DLI'].    

    aNameSymbol == #afLat     ifTrue:[^ #'AF_LAT'].    
    aNameSymbol == #'AF_LAT'  ifTrue:[^ #'AF_LAT'].    
    aNameSymbol == #lat       ifTrue:[^ #'AF_LAT'].    

    aNameSymbol == #afHylink    ifTrue:[^ #'AF_HYLINK'].    
    aNameSymbol == #'AF_HYLINK' ifTrue:[^ #'AF_HYLINK'].    
    aNameSymbol == #hylink      ifTrue:[^ #'AF_HYLINK'].    

    aNameSymbol == #afUnspec    ifTrue:[^ #'AF_UNSPEC'].    
    aNameSymbol == #'AF_UNSPEC' ifTrue:[^ #'AF_UNSPEC'].    
    aNameSymbol == #unspec      ifTrue:[^ #'AF_UNSPEC'].    

    aNameSymbol == #afRaw    ifTrue:[^ #'AF_RAW'].    
    aNameSymbol == #'AF_RAW' ifTrue:[^ #'AF_RAW'].    
    aNameSymbol == #raw      ifTrue:[^ #'AF_RAW'].    

    "/
    "/ could someone tell me which symbols are used in ST-80's SocketAddress class ?
    "/
    self error:'no more mimicri implemented yet ...'

    "Modified: / 9.1.1998 / 10:03:56 / stefan"
!

domainNameFromCode:code
    "this is a compatibility method;
     VW expects the internal unix codes here - however, in ST/X,
     symbols are expected - keeping the numeric values secret (in Socket)"

    code == #unix           ifTrue:[^ #afUnix].
    code == #'AF_UNIX'      ifTrue:[^ #afUnix].
    code == #'afUnix'       ifTrue:[^ #afUnix].

    code == #inet           ifTrue:[^ #afInet].
    code == #'AF_INET'      ifTrue:[^ #afInet].
    code == #'afInet'       ifTrue:[^ #afInet].

    code == #inet6          ifTrue:[^ #afIpV6].
    code == #'AF_INET6'     ifTrue:[^ #afIpV6].
    code == #'afIpV6'       ifTrue:[^ #afIpV6].

    code == #appletalk      ifTrue:[^ #afAppletalk].
    code == #'AF_APPLETALK' ifTrue:[^ #afAppletalk].
    code == #'afAppletalk'  ifTrue:[^ #afAppletalk].

    code == #decnet         ifTrue:[^ #afDecnet].
    code == #DECnet         ifTrue:[^ #afDecnet].
    code == #decNet         ifTrue:[^ #afDecnet].
    code == #'AF_DECNET'    ifTrue:[^ #afDecnet].
    code == #'AF_DECnet'    ifTrue:[^ #afDecnet].
    code == #'afDecnet'     ifTrue:[^ #afDecnet].

    code == #sna            ifTrue:[^ #afSna].
    code == #'AF_SNA'       ifTrue:[^ #afSna].
    code == #'afSna'        ifTrue:[^ #afSna].

    code == #xns            ifTrue:[^ #afNs].
    code == #ns             ifTrue:[^ #afNs].
    code == #'AF_NS'        ifTrue:[^ #afNs].
    code == #'afNs'         ifTrue:[^ #afNs].

    code == #ccitt          ifTrue:[^ #afCcitt].
    code == #'AF_CCITT'     ifTrue:[^ #afCcitt].
    code == #'afCcitt'      ifTrue:[^ #afCcitt].

    code == #implink      ifTrue:[^ #afImplink].    
    code == #'AF_IMPLINK' ifTrue:[^ #afImplink].    
    code == #afImplink    ifTrue:[^ #afImplink].    

    code == #pup         ifTrue:[^ #afPup].    
    code == #'AF_PUP'    ifTrue:[^ #afPup].    
    code == #afPup       ifTrue:[^ #afPup].    

    code == #chaos      ifTrue:[^ #afChaos].    
    code == #'AF_CHAOS' ifTrue:[^ #afChaos].    
    code == #afChaos    ifTrue:[^ #afChaos].    

    code == #ecma        ifTrue:[^ #afEcma].    
    code == #'AF_ECMA'   ifTrue:[^ #afEcma].    
    code == #afEcma      ifTrue:[^ #afEcma].    

    code == #datakit      ifTrue:[^ #afDatakit].    
    code == #'AF_DATAKIT' ifTrue:[^ #afDatakit].    
    code == #afDatakit    ifTrue:[^ #afDatakit].    

    code == #dli       ifTrue:[^ #afDli].    
    code == #'AF_DLI'  ifTrue:[^ #afDli].    
    code == #afDli     ifTrue:[^ #afDli].    

    code == #lat       ifTrue:[^ #afLat].    
    code == #'AF_LAT'  ifTrue:[^ #afLat].    
    code == #afLat     ifTrue:[^ #afLat].    

    code == #hylink      ifTrue:[^ #afHylink].    
    code == #'AF_HYLINK' ifTrue:[^ #afHylink].    
    code == #afHylink    ifTrue:[^ #afHylink].    

    code == #unspec      ifTrue:[^ #afUnspec].    
    code == #'AF_UNSPEC' ifTrue:[^ #afUnspec].    
    code == #afUnspec    ifTrue:[^ #afUnspec].    

    code == #raw      ifTrue:[^ #afRaw].    
    code == #'AF_RAW' ifTrue:[^ #afRaw].    
    code == #afRaw    ifTrue:[^ #afRaw].    

    "/
    "/ could someone tell me which symbols are used in ST-80's SocketAddress class ?
    "/
    self error:'no more mimicri implemented yet ...'
!

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."

    ^ OperatingSystem socketAccessor 
            getAddressInfo:hostName serviceName:serviceName 
            domain:domainArg type:typeArg protocol:protoArg flags:flags 

    "
     self getAddressInfo:'localhost' serviceName:nil 
            domain:nil type:nil protocol:nil flags:nil
     self getAddressInfo:'localhost' serviceName:nil 
            domain:#AF_INET type:#stream protocol:nil flags:nil
     self getAddressInfo:'localhost' serviceName:nil 
            domain:#AF_INET type:#stream protocol:#tcp flags:nil
     self getAddressInfo:'blurb.exept.de' serviceName:nil 
            domain:#AF_INET type:nil protocol:nil flags:nil
     self getAddressInfo:'1.2.3.4' serviceName:'6' 
            domain:#AF_INET type:nil protocol:nil flags:nil
     self getAddressInfo:'localhost' serviceName:'echo' 
            domain:#AF_INET6 type:nil protocol:nil flags:nil
    "
!

getAddressInfo:hostName serviceName:serviceName 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."

    ^ self  
        getAddressInfo:hostName serviceName:serviceName 
        domain:(self domainCode) type:typeArg protocol:protoArg flags:flags 

    "
     IPSocketAddress getAddressInfo:'localhost' serviceName:nil 
                     type:nil protocol:nil flags:nil
     IPSocketAddress getAddressInfo:'localhost' serviceName:nil 
                     type:#stream protocol:nil flags:nil
     IPSocketAddress getAddressInfo:'localhost' serviceName:nil 
                     type:#stream protocol:#tcp flags:nil
     IPSocketAddress getAddressInfo:'blurb.exept.de' serviceName:nil 
                     type:nil protocol:nil flags:nil
     IPSocketAddress getAddressInfo:'1.2.3.4' serviceName:'6' 
                     type:nil protocol:nil flags:nil
     IPSocketAddress getAddressInfo:'localhost' serviceName:'echo' 
                     type:nil protocol:nil flags:nil
    "
!

getNameInfo:socketAddress wantHostName:wantHostName wantServiceName:wantServiceName datagram:useDatagram flags:flags 
    "answer an Array containing the hostName and/or serviceName
     for a given socketAddress."

    ^ OperatingSystem socketAccessor
            getNameInfo:socketAddress wantHostName:wantHostName 
            wantServiceName:wantServiceName datagram:useDatagram flags:flags 
    
    "
     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     
    "
!

hostAddressLen
    "answer the number of bytes of the host address"

    ^ self subclassResponsibility
!

isAbstract
    "Return if this class is an abstract class.
     True is returned here for myself only; false for subclasses.
     Abstract subclasses must redefine this again."

    ^ self == SocketAddress.
!

knownClassFromCode:code
    "this is a compatibility method;
     VW expects the internal unix codes here - however, in ST/X,
     symbols are expected - keeping the numeric values secret (in Socket)"

    |cls|

    DomainToClassMapping isNil ifTrue:[
        DomainToClassMapping := IdentityDictionary new.
        "/ some are always known...
        DomainToClassMapping 
            at:#unix put:UDSocketAddress;
            at:#inet put:IPSocketAddress;
            at:#inet6 put:IPv6SocketAddress;
            at:#appletalk put:AppletalkSocketAddress;
            at:#decnet put:DecNetSocketAddress.
    ].
    cls := DomainToClassMapping at:code ifAbsent:nil.
    cls notNil ifTrue:[^ cls].

    code notNil ifTrue:[
        self allSubclassesDo:[:aSubClass|
            (aSubClass domainCode == code
            or:[aSubClass domain == code
            or:[aSubClass obsoleteDomainSymbol == code
            or:[aSubClass vwDomainSymbol == code]]])
            ifTrue:[
                DomainToClassMapping at:code put:aSubClass.
                ^ aSubClass
            ].
        ].
    ].
    ^ SocketAddress

    "
     DomainToClassMapping := nil.

     self knownClassFromCode:#'AF_UNIX' 
     self knownClassFromCode:#'AF_INET'

     self knownClassFromCode:1
     self knownClassFromCode:2

     self knownClassFromCode:#unix             obsolete ST/X codes
     self knownClassFromCode:#inet

     self knownClassFromCode:#afUnix           visualWorks codes
     self knownClassFromCode:#afInet
    "
!

obsoleteDomainSymbol
    ^ nil
!

peerNameFromPeer:peer
    ^ peer hostName
!

socketAddressSize
    "answer the OS specific size of a socket address.
     Returns nil, if the domain is not supported or invalid."

    ^ OperatingSystem socketAddressSizeOfDomain:self domain

    "
     SocketAddress socketAddressSize  
     UDSocketAddress socketAddressSize  
     IPSocketAddress socketAddressSize  
     IPv6SocketAddress socketAddressSize 
     AppletalkSocketAddress socketAddressSize  
     DecNetSocketAddress socketAddressSize     
    "
!

vwDomainSymbol
    ^ nil
! !

!SocketAddress methodsFor:'accessing'!

domainCode
    "this opaquely returns the first two bytes as a short integer;
     Notice: some systems store the domainCode in those 2 bytes,
     whereas others (IOS) store the size of the addr-struct in byte 1,
     and the actual domain code in byte 2"

    OperatingSystem isOSXlike ifTrue:[
        ^ self at:2
    ].
    ^ self unsignedInt16At:1
!

domainCode:anInteger
    "this opaquely sets the first two bytes as a short integer;
     Notice: some systems store the domainCode in those 2 bytes,
     whereas others (IOS) store the size of the addr-struct in byte 1,
     and the actual domain code in byte 2"

    OperatingSystem isOSXlike ifTrue:[
        self at:1 put:self size.
        self at:2 put:anInteger.
    ] ifFalse:[ 
        self unsignedInt16At:1 put:anInteger
    ].
!

hostAddress:addressBytes
    "generic method, subclasses usually redefine this"

    "/ the first 2-bytes (a short) is the domainCode
    self replaceBytesFrom:1+2 to:addressBytes size+2 with:addressBytes startingAt:1

    "
     IPSocketAddress hostAddress:#[193 141 12 193] port:80
    "
!

hostAddress:addressBytes port:portNr
    self hostAddress:addressBytes.
    self port:portNr

    "
     IPSocketAddress hostAddress:#[193 141 12 193] port:10
    "
!

hostAddressString
    ^ String streamContents:[:s | self printAddressOn:s ].
!

port
    self subclassResponsibility
!

port:portNr
    self subclassResponsibility
! !

!SocketAddress methodsFor:'comparing'!

sameHostAddress:aSocketAddress
    "answer true, if aSocketAddres has the same hostaddress as myself"

    ^ self class = aSocketAddress class 
        and:[self hostAddress = aSocketAddress hostAddress] 

    "
     (IPSocketAddress hostAddress:#[193 141 12 193] port:80)
        sameHostAddress:
     (IPSocketAddress hostAddress:#[193 141 12 193] port:81)
    "
! !

!SocketAddress methodsFor:'converting'!

asIPv4SocketAddressIfPossible
    ^ self
! !

!SocketAddress methodsFor:'hashing'!

hash
    "generate a SmallInteger hash for the socket address - use all the bytes"

    ^ self computeXorHashFrom:1 to:0
! !

!SocketAddress methodsFor:'printing & storing'!

displayOn:aGCOrStream
    "redefine from Collection"

    "/ what a kludge - Dolphin and Squeak mean: printOn: a stream;
    "/ old ST80 means: draw-yourself on a GC.
    (aGCOrStream isStream) ifFalse:[
        ^ super displayOn:aGCOrStream
    ].

    self printOn:aGCOrStream.

    "Modified: / 22-02-2017 / 16:59:49 / cg"
!

printAddressOn:arg 
    "raise an error: must be redefined in concrete subclass(es)"

    ^ self subclassResponsibility
!

printOn:aStream
    "append a user printed representation of the receiver to aStream.
     The format is suitable for a human - not meant to be read back."

    self printOn:aStream withPort:true
!

printOn:aStream withPort:withPort
    "append a user printed representation of the receiver to aStream.
     The format is suitable for a human - not meant to be read back."

    self printAddressOn:aStream.
    withPort ifTrue:[
        self printPortOn:aStream.
    ].
!

printPortOn:aStream 
    |port|

    (port := self port) ~~ 0 ifTrue:[
        aStream nextPut:$:.
        port printOn:aStream.
    ]
! !

!SocketAddress methodsFor:'private'!

fromBytes:aByteArray
    "Copy the internal representation of a SocketAddress to myself

     This is an internal interface!!"

    self replaceBytesFrom:1 to:(self size) with:aByteArray startingAt:1.


! !

!SocketAddress methodsFor:'queries'!

address
    ^ self subclassResponsibility
!

domain
    "anwer the domain symbol used to creat a socket"

    ^ self class domain
!

hostName
    ^ (self class getNameInfo:self wantHostName:true 
                  wantServiceName:false datagram:false flags:0) first

    "
     (IPSocketAddress hostAddress:#[127 0 0 1]) hostName        
     (IPSocketAddress hostName:'localhost') address        
     (IPSocketAddress hostName:'www.google.com') address        

     (IPSocketAddress hostAddress:#[127 0 0 1] port:7) hostName        
     (IPSocketAddress hostAddress:#[172 23 1 1] port:10) hostName  
     (IPSocketAddress hostAddress:#[172 24 1 244]) hostName 
     (IPv6SocketAddress hostAddress: #[0 0 0 0  0 0 0 0  0 0 0 0  0 0 0 1]) hostName 
     (IPv6SocketAddress hostAddress: #[0 0 0 0  0 0 0 0  0 0 0 0  0 0 0 2]) hostName 
     (IPv6SocketAddress hostAddress: #[0 0 0 0  0 0 0 0  0 0 255 255  127 0 0 1]) hostName 
     (IPv6SocketAddress hostAddress: #[0 0 0 0  0 0 0 0  0 0 255 255  172 23 1 1]) hostName 
    "
!

hostNameOrAddressString
    "answer the host name or the string representation
     of the address"

    ^ [
        self hostName.
    ] on:NameLookupError do:[:ex| |stream|
        stream := '' writeStream.
        self printAddressOn:stream.
        stream contents.
    ].

    "
        (IPSocketAddress hostName:'localhost') hostNameOrAddressString
        (IPSocketAddress hostAddress:#[192 168 8 33]) hostNameOrAddressString
    "
!

portOrName
    ^ self subclassResponsibility
!

serviceName
    ^ (self class getNameInfo:self wantHostName:false 
                  wantServiceName:true datagram:false flags:0) second

    "
     (IPSocketAddress hostAddress:#[127 0 0 1] port:7) serviceName
     (IPSocketAddress hostAddress:#[193 141 12 193] port:10) serviceName
    "
! !

!SocketAddress methodsFor:'testing'!

isIPSocketAddress
    ^ false
!

isIPv4SocketAddress
    ^ false
!

isIPv6SocketAddress
    ^ false
!

isLocal
    "answer true, if this address addresses a peer on the same host"

    ^ self subclassResponsibility
!

isMyAddress
    "answer true, if the address refers to my own host.
     Subclasses may redefine this to check the local configuration, too."

    ^ self isLocal
!

isSocketAddress
    ^ true
! !

!SocketAddress class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !