IPSocketAddress.st
author Stefan Vogel <sv@exept.de>
Fri, 28 Mar 2008 13:48:17 +0100
changeset 1937 09fb98759b4a
parent 1909 c76efba26558
child 2172 72f7e3be5d17
permissions -rw-r--r--
Clean up: - inappropriate peer and port settings - method categories

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

allForHostName:name serviceName:portNrOrName type:socketTypeSymbol
    "get a collection of new instance given a hostname, port or service and type.
     Multi-homed hosts return more than one entry
     Redefined to cache the result of the name-lookup."

    |addressList|

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

    addressList := self addressCacheAt:name.
    addressList notNil ifTrue:[
        addressList := addressList collect:[:eachSocketAddress| |sa|
            sa := eachSocketAddress copy.
            portNrOrName notNil ifTrue:[
                sa port:portNrOrName.
            ].
            sa
        ].
    ] ifFalse:[
        addressList := super allForHostName:name serviceName:portNrOrName type:socketTypeSymbol.

        self addressCacheAt:name put:addressList deepCopy.
    ].
    ^ addressList

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

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

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

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

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

localHostAddress
    "NO NO NEVER!!
     Do not use this, because:
     1. It does not work on hosts that get their IP address via dhcp (at least on unix/linux)
     2. Usually OperatingSystem>>#getHostName does not return a fully qualified domain name
     3. This does not work well on multi-homed hosts"

    <resource: #obsolete>

    self obsoleteMethodWarning:'Do not use this method'.
    ^ self hostName:(OperatingSystem getHostName).

    "
      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 addressList time|

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

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

addressCacheAt:aName put:anAddressList
    (addrCache isNil or:[addrCache size > self addressCacheSize]) ifTrue:[
        addrCache := Dictionary new.
    ].
    
    addrCache at:aName put:(anAddressList -> 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
! !

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

portOrName
    ^ self port
! !

!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.33 2008-03-28 12:48:17 stefan Exp $'
! !