prepare to use multiple servers for probing endpoints; use hardcoded list of rendezvous servers for now

master
Christian Ulrich 2020-11-14 14:18:55 +01:00
parent 5725e1ff82
commit 7831d2a6ed
No known key found for this signature in database
GPG Key ID: 8241BE099775A097
5 changed files with 99 additions and 83 deletions

View File

@ -6,6 +6,10 @@ import asyncutils
import ../../message import ../../message
import random import random
const rendezvousServers: seq[tuple[hostname: string, port: Port]] = @[
("strangeplace.net", Port(5320))
]
type type
PunchdResponse = Future[tuple[msgContent: string, sock: AsyncSocket]] PunchdResponse = Future[tuple[msgContent: string, sock: AsyncSocket]]
PunchdProgressCb = proc (future: PunchdResponse, msgContent: string) {.async.} PunchdProgressCb = proc (future: PunchdResponse, msgContent: string) {.async.}
@ -23,8 +27,9 @@ type
sock: AsyncSocket sock: AsyncSocket
outMessages: TableRef[string, Future[string]] outMessages: TableRef[string, Future[string]]
peerNotifications: FutureStream[string] peerNotifications: FutureStream[string]
publicIp: IpAddress probedIp: IpAddress
publicPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
# Punchd messages # Punchd messages
Progress* = object Progress* = object
@ -34,7 +39,8 @@ type
# Server messages # Server messages
OkGetPeerinfo* = object OkGetPeerinfo* = object
ip: string ip: string
ports: seq[uint16] localPort: Port
probedPorts: seq[Port]
OkGetEndpoint* = object OkGetEndpoint* = object
ip: IpAddress ip: IpAddress
port: Port port: Port
@ -43,9 +49,11 @@ type
recipient: string recipient: string
technique: string technique: string
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPort: Port
probedDstPorts: seq[Port]
extraArgs: string extraArgs: string
# Exceptions # Exceptions
@ -53,7 +61,7 @@ type
ServerError = object of ValueError ServerError = object of ValueError
proc usage() = proc usage() =
echo &"usage: {paramStr(0)} SERVER_HOSTNAME SERVER_PORT PEER_ID [OTHER_PEER_ID]" echo &"usage: {paramStr(0)} PEER_ID [OTHER_PEER_ID]"
proc handleServerMessages(conn: ServerConnection) {.async.} = proc handleServerMessages(conn: ServerConnection) {.async.} =
while true: while true:
@ -139,9 +147,9 @@ proc handlePeerNotifications(serverConn: ServerConnection,
let msg = parseMessage[NotifyPeer](data) let msg = parseMessage[NotifyPeer](data)
# FIXME: check if we want to receive messages from the sender # FIXME: check if we want to receive messages from the sender
echo "received message from ", msg.sender echo "received message from ", msg.sender
let srcPorts = msg.srcPorts.join(",") let probedSrcPorts = msg.probedSrcPorts.join(",")
let dstPorts = msg.dstPorts.join(",") let probedDstPorts = msg.probedDstPorts.join(",")
let req = &"{msg.technique}|{msg.srcIp}|{srcPorts}|{msg.dstIp}|{dstPorts}|{msg.extraArgs}" let req = &"{msg.technique}|{msg.srcIp}|{msg.srcPort}|{probedSrcPorts}|{msg.dstIp}|{msg.dstPort}|{probedDstPorts}|{msg.extraArgs}"
asyncCheck acceptConnection(punchdConn, "respond", req) asyncCheck acceptConnection(punchdConn, "respond", req)
except ValueError as e: except ValueError as e:
echo e.msg echo e.msg
@ -161,49 +169,50 @@ proc punchHole(punchdConn: PunchdConnection, serverConn: ServerConnection,
future.fail(e) future.fail(e)
except ValueError as e: except ValueError as e:
future.fail(e) future.fail(e)
let myPorts = serverConn.publicPorts.join(",") let myProbedPorts = serverConn.probedSrcPorts.join(",")
let peerPorts = peerInfo.ports.join(",") let probedPeerPorts = peerInfo.probedPorts.join(",")
let req = &"{technique}|{serverConn.publicIp}|{myPorts}|{peerInfo.ip}|{peerPorts}" let req = &"{technique}|{serverConn.probedIp}|{serverConn.srcPort}|{myProbedPorts}|{peerInfo.ip}|{peerInfo.localPort}|{probedPeerPorts}"
let pResp = await punchdConn.sendRequest("initiate", req, progressCb) let pResp = await punchdConn.sendRequest("initiate", req, progressCb)
result = pResp.sock result = pResp.sock
proc initServerConnection(serverHostname: string, serverPort: Port, proc getEndpoint(srcPort: Port, serverHostname: string, serverPort: Port):
probingPort: Port): Future[ServerConnection] {.async.} = Future[OkGetEndpoint] {.async.} =
result.publicPorts.add(probingPort) let sock = newAsyncSocket()
var failCount = 0 var failCount = 0
while result.publicPorts.len < 3: while true:
# FIXME: error handling
let sock = newAsyncSocket()
try: try:
sock.bindAddr(probingPort) sock.bindAddr(srcPort)
except OSError as e: except OSError as e:
if failCount == 3: if failCount == 3:
echo "raising error"
raise e raise e
failCount.inc failCount.inc
await sleepAsync(100) await sleepAsync(100)
continue continue
await sock.connect(serverHostname, serverPort) await sock.connect(serverHostname, serverPort)
let id = rand(uint32) let id = rand(uint32)
await sock.send(&"get-endpoint|{id}\n") await sock.send(&"get-endpoint|{id}\n")
let line = await sock.recvLine(maxLength = 400) let line = await sock.recvLine(maxLength = 400)
let args = line.parseArgs(3) let args = line.parseArgs(3)
assert(args[0] == "ok") assert(args[0] == "ok")
assert(args[1] == $id) assert(args[1] == $id)
let endpoint = parseMessage[OkGetEndpoint](args[2]) result = parseMessage[OkGetEndpoint](args[2])
echo "endpoint: ", endpoint let emptyLine = await sock.recvLine(maxLength = 400)
result.publicIp = endpoint.ip assert(emptyLine.len == 0)
result.publicPorts.add(endpoint.port) sock.close()
let emptyLine = await sock.recvLine(maxLength = 400)
assert(emptyLine.len == 0)
sock.close()
result.sock = await asyncnet.dial(serverHostname, serverPort) proc initServerConnection(srcPort: Port): Future[ServerConnection] {.async.} =
result.srcPort = srcPort
for r in rendezvousServers:
let endpoint = await getEndpoint(srcPort, r.hostname, r.port)
# FIXME: what if we get get different IPs from different servers
result.probedIp = endpoint.ip
result.probedSrcPorts.add(endpoint.port)
result.sock = await asyncnet.dial(rendezvousServers[0].hostname,
rendezvousServers[0].port)
result.outMessages = newTable[string, Future[string]]() result.outMessages = newTable[string, Future[string]]()
result.peerNotifications = newFutureStream[string]("initServerConnection") result.peerNotifications = newFutureStream[string]("initServerConnection")
proc runApp(serverHostname: string, serverPort: Port, peerId: string, proc runApp(peerId: string, otherPeerId: string = "") {.async.} =
otherPeerId: string = "") {.async.} =
randomize() # initialize random number generator randomize() # initialize random number generator
var punchdConn = PunchdConnection() var punchdConn = PunchdConnection()
punchdConn.sock = newAsyncSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) punchdConn.sock = newAsyncSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)
@ -214,13 +223,12 @@ proc runApp(serverHostname: string, serverPort: Port, peerId: string,
let srcPort = rand(Port(1024) .. Port.high) let srcPort = rand(Port(1024) .. Port.high)
if otherPeerId.len == 0: if otherPeerId.len == 0:
# register and wait for connections # register and wait for connections
echo &"init server connection, probing port: {srcPort}" echo &"init server connection, source port: {srcPort}"
let serverConn = await initServerConnection(serverHostname, serverPort, let serverConn = await initServerConnection(srcPort)
srcPort)
asyncCheck handleServerMessages(serverConn) asyncCheck handleServerMessages(serverConn)
asyncCheck handlePeerNotifications(serverConn, punchdConn, peerId) asyncCheck handlePeerNotifications(serverConn, punchdConn, peerId)
let myPorts = serverConn.publicPorts.join(",") let probedPorts = serverConn.probedSrcPorts.join(",")
let req = &"{peerId}|{serverConn.publicIp}|{myPorts}" let req = &"{peerId}|{serverConn.probedIp}|{serverConn.srcPort}|{probedPorts}"
echo "registering: ", req echo "registering: ", req
discard await serverConn.sendRequest("register", req) discard await serverConn.sendRequest("register", req)
while true: while true:
@ -235,8 +243,7 @@ proc runApp(serverHostname: string, serverPort: Port, peerId: string,
else: else:
# initiate a new connection # initiate a new connection
var serverConn = await initServerConnection(serverHostname, serverPort, var serverConn = await initServerConnection(srcPort)
srcPort)
asyncCheck handleServerMessages(serverConn) asyncCheck handleServerMessages(serverConn)
let sock = await punchHole(punchdConn, serverConn, peerId, otherPeerId, let sock = await punchHole(punchdConn, serverConn, peerId, otherPeerId,
"udp") "udp")
@ -247,17 +254,13 @@ proc runApp(serverHostname: string, serverPort: Port, peerId: string,
sock.close() sock.close()
proc main() = proc main() =
if paramCount() < 3 or paramCount() > 4: if paramCount() < 1 or paramCount() > 2:
usage() usage()
quit(1) quit(1)
let portNumber = paramStr(2).parseUInt if paramCount() == 2:
if portNumber > uint16.high: waitFor runApp(paramStr(1), paramStr(2))
usage()
quit(1)
if paramCount() == 4:
waitFor runApp(paramStr(1), Port(portNumber), paramStr(3), paramStr(4))
else: else:
waitFor runApp(paramStr(1), Port(portNumber), paramStr(3)) waitFor runApp(paramStr(1))
when isMainModule: when isMainModule:
main() main()

View File

@ -1,11 +1,6 @@
from net import Port from net import Port
proc predictPortRange*(dstPorts: seq[Port]): seq[Port] = proc predictPortRange*(dstPort: Port, probedDstPorts: seq[Port]): seq[Port] =
# TODO: do real port prediction # TODO: do real port prediction
result = newSeq[Port](1) result = @[dstPort]
let basePort = min(dstPorts[1].uint16,
uint16.high - (result.len - 1).uint16)
for i in 0 .. result.len - 1:
result[i] = Port(basePort + i.uint16)

View File

@ -17,15 +17,19 @@ type
InitiateRequest = object InitiateRequest = object
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPort: Port
probedDstPorts: seq[Port]
RespondRequest = object RespondRequest = object
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPort: Port
probedDstPorts: seq[Port]
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
extraArgs: string extraArgs: string
proc injectSynPackets(attempt: Attempt) {.async.} = proc injectSynPackets(attempt: Attempt) {.async.} =
@ -59,17 +63,19 @@ proc initTcpNutssPuncher*(): TcpNutssPuncher =
method parseInitiateRequest*(puncher: TcpNutssPuncher, args: string): Attempt = method parseInitiateRequest*(puncher: TcpNutssPuncher, args: string): Attempt =
let parsed = parseMessage[InitiateRequest](args) let parsed = parseMessage[InitiateRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp) let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts) let predictedDstPorts = predictPortRange(parsed.dstPort,
parsed.probedDstPorts)
let acceptFuture = newFuture[AsyncSocket]("parseInitiateRequest") let acceptFuture = newFuture[AsyncSocket]("parseInitiateRequest")
Attempt(protocol: IPPROTO_TCP, srcIp: localIp, srcPort: parsed.srcPorts[0], Attempt(protocol: IPPROTO_TCP, srcIp: localIp, srcPort: parsed.srcPort,
dstIp: parsed.dstIp, dstPorts: predictedDstPorts, dstIp: parsed.dstIp, dstPorts: predictedDstPorts,
acceptFuture: some(acceptFuture)) acceptFuture: some(acceptFuture))
method parseRespondRequest*(puncher: TcpNutssPuncher, args: string): Attempt = method parseRespondRequest*(puncher: TcpNutssPuncher, args: string): Attempt =
let parsed = parseMessage[RespondRequest](args) let parsed = parseMessage[RespondRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp) let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts) let predictedDstPorts = predictPortRange(parsed.dstPort,
Attempt(protocol: IPPROTO_TCP, srcIp: localIp, srcPort: parsed.srcPorts[0], parsed.probedDstPorts)
Attempt(protocol: IPPROTO_TCP, srcIp: localIp, srcPort: parsed.srcPort,
dstIp: parsed.dstIp, dstPorts: predictedDstPorts, dstIp: parsed.dstIp, dstPorts: predictedDstPorts,
acceptFuture: none(Future[AsyncSocket])) acceptFuture: none(Future[AsyncSocket]))

View File

@ -19,15 +19,19 @@ type
InitiateRequest = object InitiateRequest = object
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPort: Port
probedDstPorts: seq[Port]
RespondRequest = object RespondRequest = object
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPort: Port
probedDstPorts: seq[Port]
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
seqNums: seq[uint32] seqNums: seq[uint32]
TcpSyniInitiateAttempt = ref object of Attempt TcpSyniInitiateAttempt = ref object of Attempt
@ -136,19 +140,21 @@ method getProtocol*(puncher: TcpSyniPuncher): Protocol =
method parseInitiateRequest*(puncher: TcpSyniPuncher, args: string): Attempt = method parseInitiateRequest*(puncher: TcpSyniPuncher, args: string): Attempt =
let parsed = parseMessage[InitiateRequest](args) let parsed = parseMessage[InitiateRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp) let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts) let predictedDstPorts = predictPortRange(parsed.dstPort,
parsed.probedDstPorts)
TcpSyniInitiateAttempt(protocol: IPPROTO_TCP, srcIp: localIp, TcpSyniInitiateAttempt(protocol: IPPROTO_TCP, srcIp: localIp,
srcPort: parsed.srcPorts[0], dstIp: parsed.dstIp, srcPort: parsed.srcPort, dstIp: parsed.dstIp,
dstPorts: predictedDstPorts, dstPorts: predictedDstPorts,
acceptFuture: none(Future[AsyncSocket])) acceptFuture: none(Future[AsyncSocket]))
method parseRespondRequest*(puncher: TcpSyniPuncher, args: string): Attempt = method parseRespondRequest*(puncher: TcpSyniPuncher, args: string): Attempt =
let parsed = parseMessage[RespondRequest](args) let parsed = parseMessage[RespondRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp) let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts) let predictedDstPorts = predictPortRange(parsed.dstPort,
parsed.probedDstPorts)
let acceptFuture = newFuture[AsyncSocket]("parseRespondRequest") let acceptFuture = newFuture[AsyncSocket]("parseRespondRequest")
TcpSyniRespondAttempt(protocol: IPPROTO_TCP, srcIp: localIp, TcpSyniRespondAttempt(protocol: IPPROTO_TCP, srcIp: localIp,
srcPort: parsed.srcPorts[0], dstIp: parsed.dstIp, srcPort: parsed.srcPort, dstIp: parsed.dstIp,
dstPorts: predictedDstPorts, dstPorts: predictedDstPorts,
acceptFuture: some(acceptFuture), acceptFuture: some(acceptFuture),
seqNums: parsed.seqNums) seqNums: parsed.seqNums)

22
udp.nim
View File

@ -16,15 +16,19 @@ type
InitiateRequest = object InitiateRequest = object
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPort: Port
probedDstPorts: seq[Port]
RespondRequest = object RespondRequest = object
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPort: Port
probedDstPorts: seq[Port]
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPort: Port
probedSrcPorts: seq[Port]
extraArgs: string extraArgs: string
proc doInitiate(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress, proc doInitiate(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress,
@ -65,16 +69,18 @@ proc initUdpPuncher*(): UdpPuncher =
method parseInitiateRequest*(puncher: UdpPuncher, args: string): Attempt = method parseInitiateRequest*(puncher: UdpPuncher, args: string): Attempt =
let parsed = parseMessage[InitiateRequest](args) let parsed = parseMessage[InitiateRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp) let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts) let predictedDstPorts = predictPortRange(parsed.dstPort,
Attempt(protocol: IPPROTO_UDP, srcIp: localIp, srcPort: parsed.srcPorts[0], parsed.probedDstPorts)
Attempt(protocol: IPPROTO_UDP, srcIp: localIp, srcPort: parsed.srcPort,
dstIp: parsed.dstIp, dstPorts: predictedDstPorts, dstIp: parsed.dstIp, dstPorts: predictedDstPorts,
acceptFuture: none(Future[AsyncSocket])) acceptFuture: none(Future[AsyncSocket]))
method parseRespondRequest*(puncher: UdpPuncher, args: string): Attempt = method parseRespondRequest*(puncher: UdpPuncher, args: string): Attempt =
let parsed = parseMessage[RespondRequest](args) let parsed = parseMessage[RespondRequest](args)
let localIp = getPrimaryIPAddr(parsed.dstIp) let localIp = getPrimaryIPAddr(parsed.dstIp)
let predictedDstPorts = predictPortRange(parsed.dstPorts) let predictedDstPorts = predictPortRange(parsed.dstPort,
Attempt(protocol: IPPROTO_UDP, srcIp: localIp, srcPort: parsed.srcPorts[0], parsed.probedDstPorts)
Attempt(protocol: IPPROTO_UDP, srcIp: localIp, srcPort: parsed.srcPort,
dstIp: parsed.dstIp, dstPorts: predictedDstPorts, dstIp: parsed.dstIp, dstPorts: predictedDstPorts,
acceptFuture: none(Future[AsyncSocket])) acceptFuture: none(Future[AsyncSocket]))