IPSocketAddress.st
author Claus Gittinger <cg@exept.de>
Thu, 06 Sep 2007 17:31:09 +0200
changeset 1894 8bf137acc445
parent 1883 2b2f137130ae
child 1909 c76efba26558
permissions -rw-r--r--
if no unit is given in the readString, assume seconds.

"
 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' }"

SocketAddress variableByteSubclass:#IPSocketAddress
	instanceVariableNames:''
	classVariableNames:'CacheInvalidationTimeInterval AddressCacheSize NameCacheSize'
	poolDictionaries:''
	category:'OS-Sockets'
!

IPSocketAddress class instanceVariableNames:'addrCache nameCache'

"
 No other class instance variables are inherited by this class.
"
!

!IPSocketAddress 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
"
    Instances of IPSocketAddress represent tcp/ip-domain socket addresses.
    These consist of an ip address (4 bytes) and a port number.

    Notice that for performance, name and address translations are cached here
    for some time, in order to speed up heavy lookup such as when operating a webserver or similar
    application which does many address-to-hostname translations.

    For systems, when the name translation is fast (unix), you may want to disable it,
    as it prevents new addresses to be detected for some time (for example, if the own address
    changes due to a new dhcp address being aquired).
    You can also change the cacheing-interval time 
    (see caching protocol, flush*Cache, cachingIntervalTime, *CacheSize).

    [author:]
        Claus Gittinger
"
! !

!IPSocketAddress class methodsFor:'instance creation'!

addressString:aString
    "convert an address given in a dot notation like 123.456.78.9"

    ^ self hostAddress:(self hostAddressFromString:aString).

    "
     IPSocketAddress addressString:'1.2.3.4'
    "
!

hostName:name serviceName:portNrOrName type:socketTypeSymbol
    "get a new instance given a hostname, port or service and type.
     Redefined to cache the result of the name-lookup."

    |addrBytes sa|

    portNrOrName isString ifTrue:[
        ^ super hostName:name serviceName:portNrOrName type:socketTypeSymbol
    ].

    addrBytes := self addressCacheAt:name.
    addrBytes notNil ifTrue:[
        sa := self hostAddress:addrBytes.
        portNrOrName notNil ifTrue:[
            sa port:portNrOrName.
        ].
    ] ifFalse:[
        sa := super hostName:name serviceName:portNrOrName type:socketTypeSymbol.

        self addressCacheAt:name put:(sa hostAddress).
    ].
    ^ sa

    "
     SocketAddress hostName:'localhost' serviceName:10 type:#stream  
     IPSocketAddress hostName:'localhost' serviceName:'echo' type:#datagram 
     IPSocketAddress hostName:'localhost' serviceName:'echo' type:nil       
     IPSocketAddress hostName:'localhost'        
     IPv6SocketAddress hostName:'localhost'        
    "
!

localHost
    "get a new instance representing the local-host address as seen internally"

    ^ self hostAddress:(self local) port:self anyPort

    "
     self localHost hostName
     (self hostName:(OperatingSystem getHostName))      
    "

    "Modified: / 04-06-2007 / 21:34:22 / cg"
!

localHostAddress
    "get a new instance representing the local-host address as seen from the external world"

    ^ (self hostName:(OperatingSystem getHostName))

    "
     self localHost          
     self localHostAddress      
    "

    "Modified: / 04-06-2007 / 21:17:25 / cg"
! !

!IPSocketAddress class methodsFor:'addressing'!

anyAddress
    "return the anonymous addresses bytes"

    ^ #[0 0 0 0]
!

anyPort
    "return the anon port number"

    ^ 0
!

broadcastAddress
    "return the broadcast address"

    ^ #[255 255 255 255]
!

firstUnreservedPort
    "return the first unreserved port number"

    ^ 1024
!

local
    "return IN_ADDR_ANY, the address matching any local address"

    ^ #[127 0 0 1]
!

maxPort
    "return the maximum port number"

    ^ 16rffff
!

thisHost
    "return the bytes of IN_ADDR_ANY, the address matching any local address"

    ^ #[0 0 0 0]
! !

!IPSocketAddress class methodsFor:'caching'!

addressCacheAt:aHostName
    |addrAndTime addr time|

    addrCache notNil ifTrue:[
        addrAndTime := addrCache at:aHostName ifAbsent:nil.
        addrAndTime notNil ifTrue:[
            addr := addrAndTime key.
            time := addrAndTime value.
            ((Timestamp now) secondDeltaFrom:time) > self cacheInvalidationTimeInterval ifFalse:[
                ^ addr.
            ]
        ].
    ].
    ^ nil

    "Modified: / 26-04-2007 / 11:21:37 / cg"
!

addressCacheAt:aName put:aHostAddress
    (addrCache isNil or:[addrCache size > self addressCacheSize]) ifTrue:[
        addrCache := Dictionary new.
    ].
    
    addrCache at:aName put:(aHostAddress -> Timestamp now).

    "Modified: / 26-04-2007 / 11:23:26 / cg"
!

addressCacheSize
    ^ AddressCacheSize ? 50

    "Created: / 26-04-2007 / 11:22:40 / cg"
!

addressCacheSize:aNumber
    AddressCacheSize := aNumber

    "Created: / 26-04-2007 / 11:24:05 / cg"
!

cacheInvalidationTimeInterval
    ^ CacheInvalidationTimeInterval ? 30

    "Created: / 26-04-2007 / 11:21:31 / cg"
!

cacheInvalidationTimeInterval:seconds
    CacheInvalidationTimeInterval := seconds

    "Created: / 26-04-2007 / 11:29:28 / cg"
!

flushAddressCache
    addrCache := nil

    "
     self flushAddressCache
    "
!

flushNameCache
    nameCache := Dictionary new.

    "
     self flushNameCache
    "

    "Modified: / 26-04-2007 / 11:25:32 / cg"
!

nameCacheAt:aHostAddress
    |nameAndTime name time|

    nameCache notNil ifTrue:[
        nameAndTime := nameCache at:aHostAddress ifAbsent:nil.
        nameAndTime notNil ifTrue:[
            name := nameAndTime key.
            time := nameAndTime value.
            ((Timestamp now) secondDeltaFrom:time) > self cacheInvalidationTimeInterval ifFalse:[
                ^ name.
            ]
        ].
    ].
    ^ nil

    "Modified: / 26-04-2007 / 11:25:11 / cg"
!

nameCacheAt:aHostAddress put:aName
    (nameCache isNil or:[nameCache size > self nameCacheSize]) ifTrue:[
        nameCache := Dictionary new.
    ].
    
    nameCache at:aHostAddress put:(aName -> Timestamp now).

    "Modified: / 26-04-2007 / 11:23:35 / cg"
!

nameCacheSize
    ^ NameCacheSize ? 50

    "Created: / 26-04-2007 / 11:22:44 / cg"
!

nameCacheSize:aNumber
    NameCacheSize := aNumber

    "Created: / 26-04-2007 / 11:23:49 / cg"
! !

!IPSocketAddress class methodsFor:'conversion'!

hostAddressFromString:aString
    "convert an address given in a dot notation like 123.456.78.9"

    |components bytes|

    components := aString asCollectionOfSubstringsSeparatedBy:$..
    components size ~~ 4 ifTrue:[
        ^ NameLookupError raiseRequestWith:aString errorString:' - bad address string'.
    ].

    bytes := components collect:[:eachComponent| 
        Integer readFromString:eachComponent onError:[^ NameLookupError raiseRequestWith:aString errorString:' - bad address string'].
    ].

    ^ bytes asByteArray.


    "
        IPSocketAddress hostAddressFromString:'1.2.3.4'
        IPSocketAddress hostAddressFromString:'255.255.255.255'
    "
! !

!IPSocketAddress class methodsFor:'queries'!

domainSymbol

    ^ #'AF_INET'
!

obsoleteDomainSymbol
    ^ #inet
!

vwDomainSymbol
    ^ #afInet
! !

!IPSocketAddress methodsFor:'accessing'!

hostAddress
    ^ (ByteArray new:4) replaceFrom:1 to:4 with:self startingAt:5
!

hostAddress:aByteArray
    ^ self replaceFrom:5 to:8 with:aByteArray startingAt:1
!

port
    ^ self unsignedShortAt:3 bigEndian:true
!

port:aPortNr
    self unsignedShortAt:3 put:aPortNr bigEndian:true
! !

!IPSocketAddress methodsFor:'obsolete'!

address
    <resource: #obsolete>

    ^ self hostAddress
!

portOrName
    <resource: #obsolete>

    ^ self port
! !

!IPSocketAddress methodsFor:'printing & storing'!

printAddressOn:aStream
    |i1 i2|

    i1 := self adrBytesStart.
    i2 := i1 + self numAdrBytes - 1.
    i1 to:i2 do:[:i | 
        i ~~ i1 ifTrue:[
            aStream nextPut:$.
        ].
        (self at:i) printOn:aStream
    ].
! !

!IPSocketAddress methodsFor:'private'!

adrBytesStart
    ^ 5
!

numAdrBytes
    ^ 4
! !

!IPSocketAddress methodsFor:'queries'!

hostName
    |addr name|

    addr := self hostAddress.

    name := self class nameCacheAt:addr.
    name notNil ifTrue:[^ name].

    name := super hostName.
    name notNil ifTrue:[
        self class nameCacheAt:addr put:name.
    ].
    ^ name
!

networkAddress
    "Extract and return the network address from the host address."

    |highAddrByte|

    highAddrByte := self at: 5.
    (highAddrByte bitAnd: 16r80) == 0 ifTrue: [^ByteArray with: highAddrByte].
    (highAddrByte bitAnd: 16rC0) == 16r80 ifTrue: [^ByteArray with: highAddrByte with: (self at: 6)].
    (highAddrByte bitAnd: 16rC0) == 16rC0 ifTrue: [^ByteArray with: highAddrByte with: (self at: 6) with: (self at: 7)].
    ^ self error: 'unknown network class'

    "
     (IPSocketAddress hostName:'exept.eu.org') networkAddress     
     (IPSocketAddress hostName:'exept.exept.de') networkAddress    
     (IPSocketAddress hostName:'ibm.com') networkAddress          
     (IPSocketAddress hostName:'cnn.com') networkAddress          
    "
!

networkClass
    "Extract and return the network class (as a symbol) from the host address.
     Returns one of #classA #classB #classC."

    |highAddrByte|

    highAddrByte := self at: 5.
    (highAddrByte bitAnd: 16r80) == 0 ifTrue: [^#classA].
    (highAddrByte bitAnd: 16rC0) == 16r80 ifTrue: [^#classB].
    (highAddrByte bitAnd: 16rC0) == 16rC0 ifTrue: [^#classC].
    ^ self error: 'unknown network class'

    "
     (IPSocketAddress hostName:'exept.eu.org') networkClass
    "
! !

!IPSocketAddress methodsFor:'testing'!

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

    ^ self hostAddress first == 127
! !

!IPSocketAddress class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic2/IPSocketAddress.st,v 1.31 2007-06-04 19:31:38 cg Exp $'
! !