2017-10-31 14:45:15 +00:00
|
|
|
#include 'Moo.moo'.
|
|
|
|
|
2017-12-30 19:07:31 +00:00
|
|
|
##interface IPAddressInterface
|
|
|
|
##{
|
|
|
|
##}
|
|
|
|
## class XXX(Object,IPAddressInterface) {}
|
|
|
|
|
|
|
|
class(#byte) IPAddress(Object)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-12-31 03:19:50 +00:00
|
|
|
### TODO: extend the compiler
|
2017-12-30 19:07:31 +00:00
|
|
|
###class(#byte(4)) IP4Address(IPAddress) -> new basicNew always create 4 bytes. size to new: or basicNew: is ignored.
|
|
|
|
###class(#byte(16)) IP6Address(IPAddress)
|
|
|
|
|
|
|
|
class(#byte) IP4Address(IPAddress)
|
|
|
|
{
|
|
|
|
method(#class) new
|
|
|
|
{
|
|
|
|
^self basicNew: 4.
|
|
|
|
}
|
|
|
|
|
|
|
|
method(#class) fromString: str
|
|
|
|
{
|
|
|
|
^self new fromString: str.
|
|
|
|
}
|
|
|
|
|
2017-12-31 16:59:48 +00:00
|
|
|
method __fromString: str
|
2017-12-30 19:07:31 +00:00
|
|
|
{
|
|
|
|
| dots digits pos size c acc |
|
|
|
|
|
|
|
|
pos := 0.
|
|
|
|
size := str size.
|
2018-01-01 15:56:55 +00:00
|
|
|
|
2017-12-30 19:07:31 +00:00
|
|
|
acc := 0.
|
|
|
|
digits := 0.
|
|
|
|
dots := 0.
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (pos >= size)
|
|
|
|
{
|
2017-12-31 16:59:48 +00:00
|
|
|
if (dots < 3 or: [digits == 0]) { ^Error.Code.EINVAL }.
|
2017-12-30 19:07:31 +00:00
|
|
|
self basicAt: dots put: acc.
|
|
|
|
break.
|
|
|
|
}.
|
|
|
|
|
|
|
|
c := str at: pos.
|
|
|
|
pos := pos + 1.
|
|
|
|
|
|
|
|
if (c >= $0 and: [c <= $9])
|
|
|
|
{
|
2017-12-31 03:19:50 +00:00
|
|
|
acc := acc * 10 + (c asInteger - $0 asInteger).
|
2017-12-30 19:07:31 +00:00
|
|
|
if (acc > 255) { Exception signal: ('invalid IPv4 address B ' & str). }.
|
|
|
|
digits := digits + 1.
|
|
|
|
}
|
2017-12-31 16:46:52 +00:00
|
|
|
elsif (c = $.)
|
2017-12-30 19:07:31 +00:00
|
|
|
{
|
2017-12-31 16:59:48 +00:00
|
|
|
if (dots >= 3 or: [digits == 0]) { ^Error.Code.EINVAL }.
|
2017-12-30 19:07:31 +00:00
|
|
|
self basicAt: dots put: acc.
|
|
|
|
dots := dots + 1.
|
|
|
|
acc := 0.
|
|
|
|
digits := 0.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-12-31 16:59:48 +00:00
|
|
|
^Error.Code.EINVAL
|
|
|
|
### goto @label@.
|
2017-12-30 19:07:31 +00:00
|
|
|
}.
|
|
|
|
}
|
|
|
|
while (true).
|
2017-12-31 16:59:48 +00:00
|
|
|
|
|
|
|
^self.
|
|
|
|
(*
|
|
|
|
(@label@)
|
|
|
|
Exception signal: ('invalid IPv4 address ' & str).
|
|
|
|
*)
|
|
|
|
}
|
|
|
|
|
|
|
|
method fromString: str
|
|
|
|
{
|
|
|
|
if ((self __fromString: str) isError)
|
|
|
|
{
|
|
|
|
Exception signal: ('invalid IPv4 address ' & str).
|
|
|
|
}
|
2017-12-30 19:07:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class(#byte) IP6Address(IPAddress)
|
|
|
|
{
|
|
|
|
method(#class) new
|
|
|
|
{
|
|
|
|
^self basicNew: 16.
|
|
|
|
}
|
2018-01-01 15:56:55 +00:00
|
|
|
|
2017-12-30 19:07:31 +00:00
|
|
|
method(#class) fromString: str
|
|
|
|
{
|
2018-01-01 15:56:55 +00:00
|
|
|
^self new fromString: str.
|
|
|
|
}
|
|
|
|
|
|
|
|
method __fromString: str
|
|
|
|
{
|
|
|
|
| pos size ch tgpos v1 val curtok saw_xdigit colonpos |
|
|
|
|
|
|
|
|
pos := 0.
|
|
|
|
size := str size.
|
|
|
|
|
|
|
|
## handle leading :: specially
|
|
|
|
if (size > 0 and: [ (str at: pos) == $: ])
|
|
|
|
{
|
|
|
|
pos := pos + 1.
|
|
|
|
if (pos >= size or: [ (str at: pos) ~~ $:]) { ^Error.Code.EINVAL }.
|
|
|
|
}.
|
|
|
|
|
|
|
|
tgpos := 0.
|
|
|
|
curtok := pos.
|
|
|
|
val := 0.
|
|
|
|
saw_xdigit := false.
|
|
|
|
colonpos := -1.
|
|
|
|
|
|
|
|
while (pos < size)
|
|
|
|
{
|
|
|
|
ch := str at: pos.
|
|
|
|
pos := pos + 1.
|
|
|
|
|
|
|
|
v1 := ch digitValue.
|
|
|
|
if (v1 >= 0 and: [v1 <= 15])
|
|
|
|
{
|
|
|
|
val := (val bitShift: 4) bitOr: v1.
|
|
|
|
if (val > 16rFFFF) { ^Error.Code.EINVAL }.
|
|
|
|
saw_xdigit := true.
|
|
|
|
continue.
|
|
|
|
}.
|
|
|
|
|
|
|
|
if (ch == $:)
|
|
|
|
{
|
|
|
|
curtok := pos.
|
|
|
|
if (saw_xdigit not)
|
|
|
|
{
|
|
|
|
## no multiple double colons are allowed
|
|
|
|
if (colonpos >= 0) { ^Error.Code.EINVAL }.
|
|
|
|
|
|
|
|
## capture the target position when the double colons
|
|
|
|
## are encountered.
|
|
|
|
colonpos := tgpos.
|
|
|
|
continue.
|
|
|
|
}
|
|
|
|
elsif (pos >= size)
|
|
|
|
{
|
|
|
|
## a colon can't be the last character
|
|
|
|
^Error.Code.EINVAL
|
|
|
|
}.
|
|
|
|
|
|
|
|
self basicAt: tgpos put: ((val bitShift: -8) bitAnd: 16rFF).
|
|
|
|
tgpos := tgpos + 1.
|
|
|
|
self basicAt: tgpos put: (val bitAnd: 16rFF).
|
|
|
|
tgpos := tgpos + 1.
|
|
|
|
|
|
|
|
saw_xdigit := false.
|
|
|
|
val := 0.
|
|
|
|
continue.
|
|
|
|
}.
|
|
|
|
|
|
|
|
(*if (ch == $. and: [])
|
|
|
|
{
|
|
|
|
saw_xdigit := true.
|
|
|
|
break.
|
|
|
|
}.*)
|
|
|
|
|
|
|
|
|
|
|
|
## invalid character in the address
|
|
|
|
^Error.Code.EINVAL.
|
|
|
|
}.
|
|
|
|
|
|
|
|
if (saw_xdigit)
|
|
|
|
{
|
|
|
|
self basicAt: tgpos put: ((val bitShift: -8) bitAnd: 16rFF).
|
|
|
|
tgpos := tgpos + 1.
|
|
|
|
self basicAt: tgpos put: (val bitAnd: 16rFF).
|
|
|
|
tgpos := tgpos + 1.
|
|
|
|
}.
|
|
|
|
|
|
|
|
if (colonpos >= 0)
|
|
|
|
{
|
2018-01-01 15:59:34 +00:00
|
|
|
## double colon position
|
2018-01-01 15:56:55 +00:00
|
|
|
self basicMoveFrom: colonpos length: 5 to: ((self basicSize) - colonpos).
|
|
|
|
}.
|
|
|
|
|
|
|
|
tgpos dump.
|
|
|
|
self basicSize dump.
|
|
|
|
if (tgpos ~~ (self basicSize)) { 'DDDDDDDDDDDd' dump. ^Error.Code.EINVAL }.
|
|
|
|
}
|
2017-12-30 19:07:31 +00:00
|
|
|
|
2018-01-01 15:56:55 +00:00
|
|
|
method fromString: str
|
|
|
|
{
|
|
|
|
if ((self __fromString: str) isError)
|
|
|
|
{
|
|
|
|
Exception signal: ('invalid IPv6 address ' & str).
|
|
|
|
}
|
2017-12-30 19:07:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class(#byte) IP4SocketAddress(IP4Address)
|
|
|
|
{
|
|
|
|
method(#class) new
|
|
|
|
{
|
|
|
|
^self basicNew: 6.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SocketAddress(Object) from 'sck.addr'
|
|
|
|
{
|
|
|
|
##method(#primitive) family.
|
|
|
|
method(#primitive) fromString: str.
|
|
|
|
|
|
|
|
method(#class) fromString: str
|
|
|
|
{
|
|
|
|
^self new fromString: str
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##class InetSocketAddress(SocketAddress)
|
|
|
|
##{
|
|
|
|
##}
|
|
|
|
|
2017-10-18 16:15:51 +00:00
|
|
|
class Socket(Object) from 'sck'
|
|
|
|
{
|
|
|
|
var handle := -1.
|
2017-12-20 16:25:20 +00:00
|
|
|
var insem, outsem.
|
|
|
|
var(#get,#set) inputAction, outputAction.
|
2017-10-18 16:15:51 +00:00
|
|
|
|
2017-12-17 15:20:58 +00:00
|
|
|
method(#primitive) open(domain, type, proto).
|
2017-10-18 16:15:51 +00:00
|
|
|
method(#primitive) _close.
|
2017-12-24 17:36:20 +00:00
|
|
|
method(#primitive) _connect(a,b,c).
|
2017-12-20 16:25:20 +00:00
|
|
|
method(#primitive) endConnect.
|
|
|
|
|
|
|
|
method(#primitive) readBytes: bytes.
|
|
|
|
method(#primitive) writeBytes: bytes.
|
2017-10-18 16:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(* TODO: generate these domain and type from the C header *)
|
|
|
|
pooldic Socket.Domain
|
|
|
|
{
|
|
|
|
INET := 2.
|
|
|
|
INET6 := 10.
|
|
|
|
}
|
|
|
|
|
|
|
|
pooldic Socket.Type
|
|
|
|
{
|
|
|
|
STREAM := 1.
|
|
|
|
DGRAM := 2.
|
|
|
|
}
|
|
|
|
|
|
|
|
extend Socket
|
|
|
|
{
|
2017-10-30 01:11:18 +00:00
|
|
|
method(#class) new { self messageProhibited: #new }
|
|
|
|
method(#class) new: size { self messageProhibited: #new: }
|
|
|
|
|
|
|
|
method(#class) domain: domain type: type
|
|
|
|
{
|
2017-12-17 15:20:58 +00:00
|
|
|
^super new open(domain, type, 0).
|
2017-10-30 01:11:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
method close
|
2017-10-18 16:15:51 +00:00
|
|
|
{
|
2017-10-30 01:11:18 +00:00
|
|
|
if (self.handle >= 0)
|
|
|
|
{
|
|
|
|
## this primitive method may return failure.
|
2017-12-17 15:20:58 +00:00
|
|
|
## but ignore it here.
|
2017-12-28 04:58:17 +00:00
|
|
|
if (self.insem notNil)
|
2017-12-20 16:25:20 +00:00
|
|
|
{
|
2017-12-28 17:45:55 +00:00
|
|
|
System unsignal: self.insem;
|
|
|
|
removeAsyncSemaphore: self.insem.
|
2017-12-20 16:25:20 +00:00
|
|
|
self.insem := nil.
|
|
|
|
}.
|
2017-12-28 04:58:17 +00:00
|
|
|
if (self.outsem notNil)
|
2017-12-20 16:25:20 +00:00
|
|
|
{
|
2017-12-28 17:45:55 +00:00
|
|
|
System unsignal: self.outsem;
|
|
|
|
removeAsyncSemaphore: self.outsem.
|
2017-12-20 16:25:20 +00:00
|
|
|
self.outsem := nil.
|
|
|
|
}.
|
|
|
|
|
2017-10-30 01:11:18 +00:00
|
|
|
self _close.
|
|
|
|
self.handle := -1.
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 16:15:51 +00:00
|
|
|
|
2017-10-30 01:11:18 +00:00
|
|
|
method asyncConnect: connectBlock
|
|
|
|
{
|
2017-12-17 15:38:38 +00:00
|
|
|
| s1 s2 sa |
|
2017-10-31 07:13:22 +00:00
|
|
|
|
2017-10-30 01:11:18 +00:00
|
|
|
s1 := Semaphore new.
|
|
|
|
s2 := Semaphore new.
|
2017-10-18 16:15:51 +00:00
|
|
|
|
2017-10-31 07:13:22 +00:00
|
|
|
sa := [:sem |
|
2017-12-20 16:25:20 +00:00
|
|
|
'UNSIGNALLLING ...........' dump.
|
2017-12-28 17:45:55 +00:00
|
|
|
System unsignal: s1;
|
|
|
|
unsignal: s2;
|
|
|
|
removeAsyncSemaphore: s1;
|
|
|
|
removeAsyncSemaphore: s2.
|
2017-12-20 16:25:20 +00:00
|
|
|
|
|
|
|
'FINALIZING CONNECT' dump.
|
|
|
|
self endConnect.
|
|
|
|
connectBlock value: self value: (sem == s1)
|
2017-10-31 07:13:22 +00:00
|
|
|
].
|
|
|
|
|
|
|
|
s1 signalAction: sa.
|
|
|
|
s2 signalAction: sa.
|
2017-10-18 16:15:51 +00:00
|
|
|
|
2017-12-17 15:38:38 +00:00
|
|
|
[
|
2017-12-28 17:45:55 +00:00
|
|
|
System signal: s1 onOutput: self.handle;
|
|
|
|
signal: s2 afterSecs: 10;
|
|
|
|
addAsyncSemaphore: s1;
|
|
|
|
addAsyncSemaphore: s2.
|
2017-12-24 17:36:20 +00:00
|
|
|
self _connect(1, 2, 3).
|
2017-12-17 15:38:38 +00:00
|
|
|
] ifCurtailed: [
|
2017-12-18 13:34:47 +00:00
|
|
|
## rollback
|
2017-12-17 15:38:38 +00:00
|
|
|
sa value: s2.
|
|
|
|
]
|
2017-10-30 01:11:18 +00:00
|
|
|
}
|
|
|
|
|
2017-12-30 19:07:31 +00:00
|
|
|
## TODO: how to specify a timeout for an action? using another semaphore??
|
|
|
|
|
2017-12-20 16:25:20 +00:00
|
|
|
method watchInput
|
2017-10-30 01:11:18 +00:00
|
|
|
{
|
2017-12-20 16:25:20 +00:00
|
|
|
if (self.insem isNil)
|
|
|
|
{
|
|
|
|
self.insem := Semaphore new.
|
|
|
|
self.insem signalAction: [:sem | self.inputAction value: self value: true].
|
2017-12-28 17:45:55 +00:00
|
|
|
System addAsyncSemaphore: self.insem;
|
|
|
|
}.
|
|
|
|
|
|
|
|
System signal: self.insem onInput: self.handle.
|
2017-10-18 16:15:51 +00:00
|
|
|
}
|
2017-10-30 01:11:18 +00:00
|
|
|
|
2017-12-20 16:25:20 +00:00
|
|
|
method unwatchInput
|
2017-10-30 01:11:18 +00:00
|
|
|
{
|
2017-12-28 17:45:55 +00:00
|
|
|
if (self.insem notNil) { System unsignal: self.insem }.
|
2017-12-20 16:25:20 +00:00
|
|
|
}
|
2017-10-30 01:11:18 +00:00
|
|
|
|
2017-12-20 16:25:20 +00:00
|
|
|
method watchOutput
|
|
|
|
{
|
|
|
|
if (self.outsem isNil)
|
|
|
|
{
|
|
|
|
self.outsem := Semaphore new.
|
|
|
|
self.outsem signalAction: [:sem | self.outputAction value: self value: true].
|
|
|
|
System addAsyncSemaphore: self.outsem.
|
2017-12-28 17:45:55 +00:00
|
|
|
}.
|
|
|
|
|
|
|
|
System signal: self.outsem onOutput: self.handle.
|
2017-10-30 01:11:18 +00:00
|
|
|
}
|
|
|
|
|
2017-12-20 16:25:20 +00:00
|
|
|
method unwatchOutput
|
|
|
|
{
|
2017-12-28 17:45:55 +00:00
|
|
|
if (self.outsem notNil) { System unsignal: self.outsem }.
|
2017-12-20 16:25:20 +00:00
|
|
|
}
|
2017-10-18 16:15:51 +00:00
|
|
|
}
|
2017-10-30 01:11:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
class MyObject(Object)
|
|
|
|
{
|
|
|
|
method(#class) main
|
|
|
|
{
|
2017-12-20 16:25:20 +00:00
|
|
|
| s conact inact outact |
|
|
|
|
|
2017-12-30 19:07:31 +00:00
|
|
|
|
2017-12-31 03:19:50 +00:00
|
|
|
s := IP4Address fromString: '192.168.123.232'.
|
2017-12-30 19:07:31 +00:00
|
|
|
s dump.
|
2017-12-31 03:19:50 +00:00
|
|
|
s basicSize dump.
|
2018-01-01 15:56:55 +00:00
|
|
|
|
|
|
|
s := IP6Address fromString: 'fe80::c225:e9ff:fe47:b1b6'.
|
|
|
|
s dump.
|
|
|
|
s basicSize dump.
|
2017-12-31 03:19:50 +00:00
|
|
|
##s := IP6Address new.
|
|
|
|
##s dump.
|
|
|
|
##s := IP4SocketAddress new.
|
|
|
|
##s dump.
|
2017-12-30 19:07:31 +00:00
|
|
|
thisProcess terminate.
|
2017-12-20 16:25:20 +00:00
|
|
|
inact := [:sck :state |
|
|
|
|
| data n |
|
2017-12-30 19:07:31 +00:00
|
|
|
(*
|
|
|
|
end of data -> 0.
|
|
|
|
no data -> -1.
|
|
|
|
has data -> 1 or moreailure indicated by primitive function 0x55a6210 - _integer_add
|
|
|
|
|
|
|
|
error -> exception
|
|
|
|
*)
|
2017-12-20 16:25:20 +00:00
|
|
|
|
|
|
|
data := ByteArray new: 100.
|
2017-12-30 19:07:31 +00:00
|
|
|
do
|
2017-12-20 16:25:20 +00:00
|
|
|
{
|
2017-12-30 19:07:31 +00:00
|
|
|
n := sck readBytes: data.
|
|
|
|
if (n <= 0)
|
|
|
|
{
|
|
|
|
if (n == 0) { sck close }.
|
|
|
|
break.
|
|
|
|
}
|
|
|
|
elsif (n > 0)
|
|
|
|
{
|
|
|
|
(n asString & ' bytes read') dump.
|
|
|
|
data dump.
|
|
|
|
}.
|
2017-12-28 04:58:17 +00:00
|
|
|
}
|
2017-12-30 19:07:31 +00:00
|
|
|
while (true).
|
2017-12-20 16:25:20 +00:00
|
|
|
].
|
|
|
|
|
|
|
|
outact := [:sck :state |
|
|
|
|
if (state)
|
|
|
|
{
|
2017-12-28 17:45:55 +00:00
|
|
|
sck writeBytes: #[ $h, $e, $l, $l, $o, C'\n' ].
|
2017-12-20 16:25:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
}
|
|
|
|
].
|
|
|
|
|
|
|
|
conact := [:sck :state |
|
|
|
|
if (state)
|
|
|
|
{
|
|
|
|
'CONNECTED NOW.............' dump.
|
2017-12-28 17:45:55 +00:00
|
|
|
s writeBytes: #[ $h, $e, $l, $l, $o, C'\n' ].
|
2017-12-24 17:36:20 +00:00
|
|
|
|
2017-12-20 16:25:20 +00:00
|
|
|
s watchInput.
|
2017-12-24 17:36:20 +00:00
|
|
|
s watchOutput.
|
2017-12-20 16:25:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
'UNABLE TO CONNECT............' dump.
|
|
|
|
}
|
|
|
|
].
|
|
|
|
|
|
|
|
## ------------------------------------------------------
|
|
|
|
|
2017-10-30 01:11:18 +00:00
|
|
|
[
|
|
|
|
s := Socket domain: Socket.Domain.INET type: Socket.Type.STREAM.
|
2017-12-20 16:25:20 +00:00
|
|
|
s inputAction: inact; outputAction: outact.
|
|
|
|
s asyncConnect: conact.
|
2017-10-30 01:11:18 +00:00
|
|
|
|
2017-10-31 14:45:15 +00:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
System handleAsyncEvent.
|
|
|
|
}.
|
2017-10-30 01:11:18 +00:00
|
|
|
s close dump.
|
2017-12-20 16:25:20 +00:00
|
|
|
|
2017-10-30 01:11:18 +00:00
|
|
|
] on: Exception do: [:ex | ('Exception - ' & ex messageText) dump ].
|
|
|
|
|
|
|
|
'----- END OF MAIN ------' dump.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|