2024-06-11 11:56:37 +00:00
|
|
|
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
2021-09-13 12:54:06 +00:00
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
{.push raises: [].}
|
2021-09-13 12:54:06 +00:00
|
|
|
|
|
|
|
import
|
2022-07-09 08:55:15 +00:00
|
|
|
std/[tables, options, hashes, math],
|
2022-06-17 20:45:37 +00:00
|
|
|
chronos, chronicles,
|
2021-10-28 09:41:43 +00:00
|
|
|
./utp_router,
|
2021-09-13 12:54:06 +00:00
|
|
|
../keys
|
|
|
|
|
|
|
|
logScope:
|
2022-12-06 13:54:03 +00:00
|
|
|
topics = "eth utp"
|
2021-09-13 12:54:06 +00:00
|
|
|
|
|
|
|
type
|
2022-11-16 16:44:00 +00:00
|
|
|
# For now utp protocol is tied to udp transport, but ultimately we would like to
|
2021-09-13 12:54:06 +00:00
|
|
|
# abstract underlying transport to be able to run utp over udp, discoveryv5 or
|
|
|
|
# maybe some test transport
|
|
|
|
UtpProtocol* = ref object
|
|
|
|
transport: DatagramTransport
|
2021-10-28 09:41:43 +00:00
|
|
|
utpRouter: UtpRouter[TransportAddress]
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
SendCallbackBuilder* =
|
|
|
|
proc (d: DatagramTransport):
|
|
|
|
SendCallback[TransportAddress] {.gcsafe, raises: [].}
|
2022-02-10 07:05:44 +00:00
|
|
|
|
2023-06-22 16:13:16 +00:00
|
|
|
chronicles.formatIt(TransportAddress): $it
|
|
|
|
|
2021-10-11 12:16:06 +00:00
|
|
|
# This should probably be defined in TransportAddress module, as hash function should
|
2022-11-16 16:44:00 +00:00
|
|
|
# be consistent with equality function
|
2021-10-11 12:16:06 +00:00
|
|
|
# in nim zero arrays always have hash equal to 0, irrespectively of array size, to
|
2022-11-16 16:44:00 +00:00
|
|
|
# avoid clashes between different types of addresses, each type have mixed different
|
2021-10-11 12:16:06 +00:00
|
|
|
# magic number
|
|
|
|
proc hash(x: TransportAddress): Hash =
|
|
|
|
var h: Hash = 0
|
|
|
|
case x.family
|
|
|
|
of AddressFamily.None:
|
|
|
|
h = h !& 31
|
|
|
|
!$h
|
|
|
|
of AddressFamily.IPv4:
|
|
|
|
h = h !& x.address_v4.hash
|
|
|
|
h = h !& x.port.hash
|
|
|
|
h = h !& 37
|
|
|
|
!$h
|
|
|
|
of AddressFamily.IPv6:
|
|
|
|
h = h !& x.address_v6.hash
|
|
|
|
h = h !& x.port.hash
|
|
|
|
h = h !& 41
|
|
|
|
!$h
|
|
|
|
of AddressFamily.Unix:
|
|
|
|
h = h !& x.address_un.hash
|
|
|
|
h = h !& 43
|
|
|
|
!$h
|
|
|
|
|
|
|
|
# Required to use socketKey as key in hashtable
|
2021-10-28 09:41:43 +00:00
|
|
|
proc hash(x: UtpSocketKey[TransportAddress]): Hash =
|
2021-10-11 12:16:06 +00:00
|
|
|
var h = 0
|
|
|
|
h = h !& x.remoteAddress.hash
|
|
|
|
h = h !& x.rcvId.hash
|
|
|
|
!$h
|
|
|
|
|
2021-09-13 12:54:06 +00:00
|
|
|
proc processDatagram(transp: DatagramTransport, raddr: TransportAddress):
|
2024-01-23 17:41:38 +00:00
|
|
|
Future[void] {.async: (raises: []).} =
|
2021-10-28 09:41:43 +00:00
|
|
|
let router = getUserData[UtpRouter[TransportAddress]](transp)
|
2021-09-13 12:54:06 +00:00
|
|
|
# TODO: should we use `peekMessage()` to avoid allocation?
|
|
|
|
let buf = try: transp.getMessage()
|
2024-01-12 08:49:06 +00:00
|
|
|
except TransportError as e:
|
|
|
|
trace "Error reading datagram msg: ", error = e.msg
|
2021-09-13 12:54:06 +00:00
|
|
|
# This is likely to be local network connection issues.
|
|
|
|
return
|
2024-01-23 17:41:38 +00:00
|
|
|
try:
|
|
|
|
await processIncomingBytes[TransportAddress](router, buf, raddr)
|
|
|
|
except CancelledError:
|
|
|
|
debug "processIncomingBytes canceled"
|
2021-09-13 12:54:06 +00:00
|
|
|
|
2021-10-28 09:41:43 +00:00
|
|
|
proc initSendCallback(t: DatagramTransport): SendCallback[TransportAddress] =
|
|
|
|
return (
|
2024-01-23 17:41:38 +00:00
|
|
|
proc (
|
|
|
|
to: TransportAddress, data: seq[byte]
|
|
|
|
) {.raises: [], gcsafe.} =
|
|
|
|
let fut = t.sendTo(to, data)
|
|
|
|
let cb = proc(data: pointer) {.gcsafe.} =
|
|
|
|
if fut.failed:
|
|
|
|
debug "uTP send failed", msg = fut.readError.msg
|
|
|
|
fut.addCallback cb
|
2021-10-28 09:41:43 +00:00
|
|
|
)
|
2021-09-13 12:54:06 +00:00
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
proc new*(
|
2022-08-12 13:09:59 +00:00
|
|
|
T: type UtpProtocol,
|
|
|
|
acceptConnectionCb: AcceptConnectionCallback[TransportAddress],
|
|
|
|
address: TransportAddress,
|
|
|
|
udata: pointer = nil,
|
|
|
|
socketConfig: SocketConfig = SocketConfig.init(),
|
|
|
|
allowConnectionCb: AllowConnectionCallback[TransportAddress] = nil,
|
|
|
|
sendCallbackBuilder: SendCallbackBuilder = nil,
|
2023-05-10 13:50:04 +00:00
|
|
|
rng = newRng()): UtpProtocol {.raises: [CatchableError].} =
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
doAssert(not(isNil(acceptConnectionCb)))
|
2021-10-28 09:41:43 +00:00
|
|
|
|
|
|
|
let router = UtpRouter[TransportAddress].new(
|
|
|
|
acceptConnectionCb,
|
2021-11-18 09:05:56 +00:00
|
|
|
allowConnectionCb,
|
2022-08-12 13:09:59 +00:00
|
|
|
udata,
|
2021-10-28 09:41:43 +00:00
|
|
|
socketConfig,
|
|
|
|
rng
|
2021-10-25 07:58:13 +00:00
|
|
|
)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
|
|
|
let ta = newDatagramTransport(processDatagram, udata = router, local = address)
|
2022-02-10 07:05:44 +00:00
|
|
|
|
|
|
|
if (sendCallbackBuilder == nil):
|
|
|
|
router.sendCb = initSendCallback(ta)
|
|
|
|
else:
|
|
|
|
router.sendCb = sendCallbackBuilder(ta)
|
|
|
|
|
2021-10-28 09:41:43 +00:00
|
|
|
UtpProtocol(transport: ta, utpRouter: router)
|
2021-09-13 12:54:06 +00:00
|
|
|
|
2022-08-12 13:09:59 +00:00
|
|
|
proc new*(
|
|
|
|
T: type UtpProtocol,
|
|
|
|
acceptConnectionCb: AcceptConnectionCallback[TransportAddress],
|
|
|
|
address: TransportAddress,
|
|
|
|
udata: ref,
|
|
|
|
socketConfig: SocketConfig = SocketConfig.init(),
|
|
|
|
allowConnectionCb: AllowConnectionCallback[TransportAddress] = nil,
|
|
|
|
sendCallbackBuilder: SendCallbackBuilder = nil,
|
2023-05-10 13:50:04 +00:00
|
|
|
rng = newRng()): UtpProtocol {.raises: [CatchableError].} =
|
2022-08-12 13:09:59 +00:00
|
|
|
GC_ref(udata)
|
|
|
|
UtpProtocol.new(
|
|
|
|
acceptConnectionCb,
|
|
|
|
address,
|
|
|
|
cast[pointer](udata),
|
|
|
|
socketConfig,
|
|
|
|
allowConnectionCb,
|
|
|
|
sendCallbackBuilder,
|
|
|
|
rng
|
|
|
|
)
|
|
|
|
|
2024-06-11 11:56:37 +00:00
|
|
|
proc shutdownWait*(p: UtpProtocol): Future[void] {.async: (raises: []).} =
|
|
|
|
## Closes all managed utp sockets and then underlying transport
|
2021-11-09 14:29:59 +00:00
|
|
|
await p.utpRouter.shutdownWait()
|
2021-10-25 07:58:13 +00:00
|
|
|
await p.transport.closeWait()
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-06-11 11:56:37 +00:00
|
|
|
proc connectTo*(
|
|
|
|
r: UtpProtocol, address: TransportAddress
|
|
|
|
): Future[ConnectionResult[TransportAddress]] {.async: (raw: true, raises: [CancelledError]).} =
|
|
|
|
r.utpRouter.connectTo(address)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-06-11 11:56:37 +00:00
|
|
|
proc connectTo*(
|
|
|
|
r: UtpProtocol, address: TransportAddress, connectionId: uint16
|
|
|
|
): Future[ConnectionResult[TransportAddress]] {.async: (raw: true, raises: [CancelledError]).} =
|
|
|
|
r.utpRouter.connectTo(address, connectionId)
|
2021-11-18 09:05:56 +00:00
|
|
|
|
2021-10-28 09:41:43 +00:00
|
|
|
proc openSockets*(r: UtpProtocol): int =
|
|
|
|
len(r.utpRouter)
|