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)
|
||||
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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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