first implementation of UdpPuncher (untested)

This commit is contained in:
Christian Ulrich 2020-10-25 21:57:09 +01:00
parent b9dc7dabaf
commit 383260c95e
No known key found for this signature in database
GPG Key ID: 8241BE099775A097
3 changed files with 114 additions and 1 deletions

View File

@ -239,7 +239,7 @@ proc runApp(serverHostname: string, serverPort: Port, peerId: string,
srcPort)
asyncCheck handleServerMessages(serverConn)
let sock = await punchHole(punchdConn, serverConn, peerId, otherPeerId,
"tcp-nutss")
"udp")
echo "connected!"
await sock.send("ping")
let msg = await sock.recv(4)

View File

@ -8,6 +8,7 @@ import options
import tables
import tcp_syni
import tcp_nutss
import udp
type
Punchd = ref object
@ -151,6 +152,7 @@ proc main() =
let punchd = Punchd(unixSocket: unixSocket)
punchd.punchers["tcp-syni"] = initTcpSyniPuncher()
punchd.punchers["tcp-nutss"] = initTcpNutssPuncher()
punchd.punchers["udp"] = initUdpPuncher()
asyncCheck handleUsers(punchd)
try:
runForever()

111
udp.nim Normal file
View File

@ -0,0 +1,111 @@
from nativesockets import Protocol, SockType
from net import IpAddress, Port, `$`, `==`
import asyncdispatch
import asyncnet
import message
import options
import port_prediction
import puncher
import strformat
import utils
export puncher
type
UdpPuncher* = ref object of Puncher
InitiateRequest = object
srcIp: IpAddress
srcPorts: seq[Port]
dstIp: IpAddress
dstPorts: seq[Port]
RespondRequest = object
dstIp: IpAddress
dstPorts: seq[Port]
srcIp: IpAddress
srcPorts: seq[Port]
extraArgs: string
proc doInitiate(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress,
dstPort: Port, initiateFuture: Future[AsyncSocket]) {.async.} =
let sock = newAsyncSocket(sockType = SOCK_DGRAM, protocol = IPPROTO_UDP)
sock.bindAddr(srcPort, $srcIp)
await sock.connect($dstIp, dstPort)
await sock.send("SYN")
let recvFuture = sock.recv(6) # "SYNACK"
await recvFuture or sleepAsync(Timeout)
if recvFuture.finished():
let msg = recvFuture.read()
echo &"connection {srcIp}:{srcPort.int} -> {dstIp}:{dstPort.int} succeeded: ", msg
await sock.send("ACK")
initiateFuture.complete(sock)
else:
echo &"connection {srcIp}:{srcPort.int} -> {dstIp}:{dstPort.int} timed out"
proc doRespond(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress,
dstPort: Port, respondFuture: Future[AsyncSocket]) {.async.} =
let sock = newAsyncSocket(sockType = SOCK_DGRAM, protocol = IPPROTO_UDP)
sock.bindAddr(srcPort, $srcIp)
await sock.connect($dstIp, dstPort)
await sock.send("SYNACK")
let recvFuture = sock.recv(3) # "ACK"
await recvFuture or sleepAsync(Timeout)
if recvFuture.finished():
let msg = recvFuture.read()
echo &"connection {srcIp}:{srcPort.int} -> {dstIp}:{dstPort.int} succeeded: ", msg
respondFuture.complete(sock)
else:
echo &"connection {srcIp}:{srcPort.int} -> {dstIp}:{dstPort.int} timed out"
proc initUdpPuncher*(): UdpPuncher =
UdpPuncher()
method parseInitiateRequest*(puncher: UdpPuncher, args: string): Attempt =
let parsed = parseMessage[InitiateRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts)
Attempt(protocol: IPPROTO_UDP, srcIp: localIp, srcPort: parsed.srcPorts[0],
dstIp: parsed.dstIp, dstPorts: predictedDstPorts,
acceptFuture: none(Future[AsyncSocket]))
method parseRespondRequest*(puncher: UdpPuncher, args: string): Attempt =
let parsed = parseMessage[RespondRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts)
Attempt(protocol: IPPROTO_UDP, srcIp: localIp, srcPort: parsed.srcPorts[0],
dstIp: parsed.dstIp, dstPorts: predictedDstPorts,
acceptFuture: none(Future[AsyncSocket]))
method initiate*(puncher: UdpPuncher, attempt: Attempt,
progress: PunchProgressCb): Future[AsyncSocket] {.async.} =
assert(attempt.acceptFuture.isNone(), "expected attempt without acceptFuture")
try:
let initiateFuture = newFuture[AsyncSocket]("initiate")
for dstPort in attempt.dstPorts:
asyncCheck doInitiate(attempt.srcIp, attempt.srcPort, attempt.dstIp,
dstPort, initiateFuture)
await progress("")
await initiateFuture or sleepAsync(Timeout)
if initiateFuture.finished():
result = initiateFuture.read()
else:
raise newException(PunchHoleError, "timeout")
except OSError as e:
raise newException(PunchHoleError, e.msg)
method respond*(puncher: UdpPuncher, attempt: Attempt):
Future[AsyncSocket] {.async.} =
assert(attempt.acceptFuture.isNone(), "expected attempt without acceptFuture")
try:
let respondFuture = newFuture[AsyncSocket]("respond")
for dstPort in attempt.dstPorts:
asyncCheck doRespond(attempt.srcIp, attempt.srcPort, attempt.dstIp,
dstPort, respondFuture)
await respondFuture or sleepAsync(Timeout)
if respondFuture.finished():
result = respondFuture.read()
else:
raise newException(PunchHoleError, "timeout")
except OSError as e:
raise newException(PunchHoleError, e.msg)