Skip to content

Commit

Permalink
Add support to Unix Domain Sockets
Browse files Browse the repository at this point in the history
There was already some degree of support, but as seen in this blog
post a few more changes were required:

https://samadhiweb.com/blog/2013.07.27.unixdomainsockets.html

fixes pharo-project#4046
  • Loading branch information
fcr-- committed Oct 29, 2023
1 parent c0bd11f commit 3e0a20d
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 3 deletions.
30 changes: 28 additions & 2 deletions src/Network-Kernel/NetNameResolver.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ NetNameResolver class >> addressForName: hostName timeout: secs [
^ result asSocketAddress
]

{ #category : 'lookups' }
NetNameResolver class >> addressForUnixDomain: path [
"Create a socket address that can be used on Socket newIPC connectTo:"

| deadline size result |
deadline := Time millisecondClockValue -> (60 * 1000).
result := nil.

self resolverMutex critical: [
(self waitForResolverReadyUntil: deadline)
ifTrue: [
self primGetAddressInfoHost: '' service: path flags: 0 family: 1 type: 0 protocol: 0.
size := self primGetAddressInfoSize.
result := ByteArray new: size withAll: 0.
self primGetAddressInfoResult: result.
^ result ]
ifFalse: [ ^ NameLookupFailure signalFor: path ] ]
]

{ #category : 'address string utils' }
NetNameResolver class >> addressFromIPv6String: aString [
"Return a ByteArray corresponding to an IPv6 address in text format"
Expand Down Expand Up @@ -575,11 +594,18 @@ NetNameResolver class >> stringFromAddress: addr [
"Return a string representing the given host address as four decimal bytes delimited with decimal points."
"NetNameResolver stringFromAddress: NetNameResolver localHostAddress"

| s |
| s size |
(addr isKindOf: SocketAddress) ifTrue: [^addr printString copyUpTo: $( ].

"If size is AddressHeaderSize + sizeof (struct sockaddr_un), it's likely IPC"
size := addr size.
(size = 118) ifTrue: [ | limit |
limit := addr indexOf: 0 startingAt: 11 ifAbsent: size + 1.
^ (addr copyFrom: 11 to: limit - 1) asString ].

"If the incoming addr is a size 16 ByteArray, we assume it is
representing an IPv6 address."
(addr size = 16) ifTrue: [ ^ self stringFromIPv6Address: addr ].
(size = 16) ifTrue: [ ^ self stringFromIPv6Address: addr ].

"Otherwise write out in IPv4 format"
s := WriteStream on: ''.
Expand Down
57 changes: 56 additions & 1 deletion src/Network-Kernel/Socket.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ Class {
#classVars : [
'Connected',
'DeadServer',
'IPv4SocketDomain',
'IPv6SocketDomain',
'InvalidSocket',
'OtherEndClosed',
'Registry',
'TCPSocketType',
'ThisEndClosed',
'UDPSocketType',
'Unconnected',
'UnixSocketDomain',
'UnspecSocketDomain',
'WaitingForConnection'
],
#category : 'Network-Kernel-Base',
Expand Down Expand Up @@ -62,6 +66,12 @@ Socket class >> initialize [
"Socket Types"
TCPSocketType := 0.
UDPSocketType := 1.

"Socket Domains"
UnspecSocketDomain := 0.
UnixSocketDomain := 1.
IPv4SocketDomain := 2.
IPv6SocketDomain := 10.

"Socket Status Values"
InvalidSocket := -1.
Expand Down Expand Up @@ -172,6 +182,15 @@ Socket class >> newAcceptCheck [
socket destroy
]

{ #category : 'instance creation' }
Socket class >> newIPC [
"Create a Unix Domain Socket and initialize it for interprocess communication"

self initializeNetwork.
^ [ super new initialize: TCPSocketType withDomain: UnixSocketDomain ]
repeatWithGCIf: [ :socket | socket isValid not ]
]

{ #category : 'instance creation' }
Socket class >> newTCP [
"Create a socket and initialize it for TCP"
Expand Down Expand Up @@ -476,6 +495,19 @@ Socket >> closeAndDestroy: timeoutSeconds [
self destroy]
]

{ #category : 'connection open/close' }
Socket >> connectNonBlockingTo: hostAddress [
"Initiate a connection to the given port at the given host address. This operation will return immediately; follow it with waitForConnectionUntil: to wait until the connection is established."

| status |
self initializeNetwork.
status := self primSocketConnectionStatus: socketHandle.
(status == Unconnected)
ifFalse: [InvalidSocketStatusException signal: 'Socket status must Unconnected before opening a new connection'].

self primSocket: socketHandle connectTo: hostAddress
]

{ #category : 'connection open/close' }
Socket >> connectNonBlockingTo: hostAddress port: port [
"Initiate a connection to the given port at the given host address. This operation will return immediately; follow it with waitForConnectionUntil: to wait until the connection is established."
Expand All @@ -489,6 +521,13 @@ Socket >> connectNonBlockingTo: hostAddress port: port [
self primSocket: socketHandle connectTo: hostAddress port: port
]

{ #category : 'public - line' }
Socket >> connectTo: socketAddress [
"Connect method that can be used with Unix Domain Sockets."

self connectTo: socketAddress waitForConnectionFor: self class standardTimeout
]

{ #category : 'connection open/close' }
Socket >> connectTo: hostAddress port: port [
"Initiate a connection to the given port at the given host address.
Expand All @@ -508,6 +547,17 @@ Socket >> connectTo: hostAddress port: port waitForConnectionFor: timeout [
, (NetNameResolver stringFromAddress: hostAddress) , ':' , port asString]
]

{ #category : 'connection open/close' }
Socket >> connectTo: socketAddress waitForConnectionFor: timeout [
"Initiate a connection to the given socket address
Waits until the connection is established or time outs."

self connectNonBlockingTo: socketAddress.
self waitForConnectionFor: timeout ifTimedOut: [
ConnectionTimedOut signal: 'Cannot connect to ' ,
(NetNameResolver stringFromAddress: socketAddress) ]
]

{ #category : 'connection open/close' }
Socket >> connectToHostNamed: hostName port: portNumber [
| serverIP |
Expand Down Expand Up @@ -620,6 +670,11 @@ foo waitForConnectionUntil: (Socket standardDeadline).

{ #category : 'initialization' }
Socket >> initialize: socketType [
self initialize: socketType withDomain: UnspecSocketDomain.
]

{ #category : 'initialization' }
Socket >> initialize: socketType withDomain: socketDomain [
"Initialize a new socket handle. If socket creation fails, socketHandle will be set to nil."

| semaIndex readSemaIndex writeSemaIndex |
Expand All @@ -631,7 +686,7 @@ Socket >> initialize: socketType [
readSemaIndex := Smalltalk registerExternalObject: readSemaphore.
writeSemaIndex := Smalltalk registerExternalObject: writeSemaphore.
socketHandle := self
primSocketCreateNetwork: 0
primSocketCreateNetwork: socketDomain
type: socketType
receiveBufferSize: 8000
sendBufSize: 8000
Expand Down

0 comments on commit 3e0a20d

Please sign in to comment.