first implementation of UdpPuncher (untested)
This commit is contained in:
parent
b9dc7dabaf
commit
383260c95e
|
@ -239,7 +239,7 @@ proc runApp(serverHostname: string, serverPort: Port, peerId: string,
|
||||||
srcPort)
|
srcPort)
|
||||||
asyncCheck handleServerMessages(serverConn)
|
asyncCheck handleServerMessages(serverConn)
|
||||||
let sock = await punchHole(punchdConn, serverConn, peerId, otherPeerId,
|
let sock = await punchHole(punchdConn, serverConn, peerId, otherPeerId,
|
||||||
"tcp-nutss")
|
"udp")
|
||||||
echo "connected!"
|
echo "connected!"
|
||||||
await sock.send("ping")
|
await sock.send("ping")
|
||||||
let msg = await sock.recv(4)
|
let msg = await sock.recv(4)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import options
|
||||||
import tables
|
import tables
|
||||||
import tcp_syni
|
import tcp_syni
|
||||||
import tcp_nutss
|
import tcp_nutss
|
||||||
|
import udp
|
||||||
|
|
||||||
type
|
type
|
||||||
Punchd = ref object
|
Punchd = ref object
|
||||||
|
@ -151,6 +152,7 @@ proc main() =
|
||||||
let punchd = Punchd(unixSocket: unixSocket)
|
let punchd = Punchd(unixSocket: unixSocket)
|
||||||
punchd.punchers["tcp-syni"] = initTcpSyniPuncher()
|
punchd.punchers["tcp-syni"] = initTcpSyniPuncher()
|
||||||
punchd.punchers["tcp-nutss"] = initTcpNutssPuncher()
|
punchd.punchers["tcp-nutss"] = initTcpNutssPuncher()
|
||||||
|
punchd.punchers["udp"] = initUdpPuncher()
|
||||||
asyncCheck handleUsers(punchd)
|
asyncCheck handleUsers(punchd)
|
||||||
try:
|
try:
|
||||||
runForever()
|
runForever()
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue