adapt punchd messages to README

This commit is contained in:
Christian Ulrich 2020-10-22 00:22:11 +02:00
parent 239ddd64b1
commit 0f21a63a81
No known key found for this signature in database
GPG Key ID: 8241BE099775A097
5 changed files with 86 additions and 74 deletions

View File

@ -94,10 +94,10 @@ progress|ID|TECHNIQUE|IP_FROM|PORTS_FROM|IP_TO|PORTS_TO|EXTRA_ARGS
error|ID|ERROR_MSG error|ID|ERROR_MSG
``` ```
The fields in the "progress" message are the same that can be found in "respond" The fields in the "progress" message are the same that can be found in the
message described above. This is because the application is expected to forward "respond" message described above. This is because the application is expected
them to the other peer through the rendezvous server so the other peer can call to forward them to the other peer through the rendezvous server so the other
"respond" on its ``punchd`` instance. peer can call "respond" on its ``punchd`` instance.
## Example ## Example

View File

@ -41,12 +41,12 @@ type
NotifyPeer* = object NotifyPeer* = object
sender: string sender: string
recipient: string recipient: string
command: string technique: string
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPorts: seq[Port]
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPorts: seq[Port]
#seqNumbers: seq[uint32] extraArgs: string
# Exceptions # Exceptions
PunchdError = object of ValueError # FIXME: not used yet PunchdError = object of ValueError # FIXME: not used yet
@ -141,15 +141,14 @@ proc handlePeerNotifications(serverConn: ServerConnection,
echo "received message from ", msg.sender echo "received message from ", msg.sender
let srcPorts = msg.srcPorts.join(",") let srcPorts = msg.srcPorts.join(",")
let dstPorts = msg.dstPorts.join(",") let dstPorts = msg.dstPorts.join(",")
#let seqNumbers = msg.seqNumbers.join(",") let req = &"{msg.srcIp}|{srcPorts}|{msg.dstIp}|{dstPorts}|{msg.extraData}"
let req = &"{msg.srcIp}|{srcPorts}|{msg.dstIp}|{dstPorts}"
asyncCheck acceptConnection(punchdConn, msg.command, req) asyncCheck acceptConnection(punchdConn, msg.command, req)
except ValueError as e: except ValueError as e:
echo e.msg echo e.msg
discard discard
proc punchHole(punchdConn: PunchdConnection, serverConn: ServerConnection, proc punchHole(punchdConn: PunchdConnection, serverConn: ServerConnection,
peerId: string, otherPeerId: string): peerId: string, otherPeerId: string, technique: string):
Future[AsyncSocket] {.async.} = Future[AsyncSocket] {.async.} =
let sResp = await serverConn.sendRequest("get-peerinfo", otherPeerId) let sResp = await serverConn.sendRequest("get-peerinfo", otherPeerId)
let peerInfo = parseMessage[OkGetPeerinfo](sResp) let peerInfo = parseMessage[OkGetPeerinfo](sResp)
@ -164,8 +163,8 @@ proc punchHole(punchdConn: PunchdConnection, serverConn: ServerConnection,
future.fail(e) future.fail(e)
let myPorts = serverConn.publicPorts.join(",") let myPorts = serverConn.publicPorts.join(",")
let peerPorts = peerInfo.ports.join(",") let peerPorts = peerInfo.ports.join(",")
let req = &"{serverConn.publicIp}|{myPorts}|{peerInfo.ip}|{peerPorts}" let req = &"{technique}|{serverConn.publicIp}|{myPorts}|{peerInfo.ip}|{peerPorts}"
let pResp = await punchdConn.sendRequest("tcp-nutss-initiate", req, progressCb) let pResp = await punchdConn.sendRequest("initiate", req, progressCb)
result = pResp.sock result = pResp.sock
proc initServerConnection(serverHostname: string, serverPort: Port, proc initServerConnection(serverHostname: string, serverPort: Port,
@ -239,7 +238,8 @@ proc runApp(serverHostname: string, serverPort: Port, peerId: string,
var serverConn = await initServerConnection(serverHostname, serverPort, var serverConn = await initServerConnection(serverHostname, serverPort,
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")
echo "connected!" echo "connected!"
await sock.send("ping") await sock.send("ping")
let msg = await sock.recv(4) let msg = await sock.recv(4)

View File

@ -3,8 +3,8 @@ from nativesockets import Domain, SockType, Protocol
from net import IpAddress, Port, `$` from net import IpAddress, Port, `$`
import asyncutils import asyncutils
import message import message
import tcp_syni_connect import tcp_syni_initiator
import tcp_syni_accept import tcp_syni_responder
import tcp_nutss_initiator import tcp_nutss_initiator
import tcp_nutss_responder import tcp_nutss_responder
@ -14,38 +14,39 @@ from nativesockets import setSockOptInt
type type
Punchd = ref object Punchd = ref object
unixSocket: AsyncSocket unixSocket: AsyncSocket
tcpSyniCP: TcpSyniConnectPuncher tcpSyniInitiator: TcpSyniInitiator
tcpSyniAP: TcpSyniAcceptPuncher tcpSyniResponder: TcpSyniResponder
tcpNutssInitiator: TcpNutssInitiator tcpNutssInitiator: TcpNutssInitiator
tcpNutssResponder: TcpNutssResponder tcpNutssResponder: TcpNutssResponder
Sigint = object of CatchableError Sigint = object of CatchableError
# Requests # Requests
TcpSyniConnect = object InitiateTcpSyni = object
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPorts: seq[Port]
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPorts: seq[Port]
TcpSyniAccept = object RespondTcpSyni = object
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPorts: seq[Port]
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPorts: seq[Port]
seqNums: seq[uint32] seqNums: seq[uint32]
TcpNutssInitiate = object InitiateTcpNutss = object
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPorts: seq[Port]
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPorts: seq[Port]
TcpNutssRespond = object RespondTcpNutss = object
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPorts: seq[Port]
srcIp: IpAddress srcIp: IpAddress
srcPorts: seq[Port] srcPorts: seq[Port]
extraData: string
const PunchdSocket = "/tmp/punchd.socket" const PunchdSocket = "/tmp/punchd.socket"
@ -63,40 +64,51 @@ proc handleRequest(punchd: Punchd, line: string,
var id: string var id: string
var sock: AsyncSocket var sock: AsyncSocket
try: try:
let args = line.parseArgs(3) let args = line.parseArgs(4)
id = args[1] id = args[1]
case args[0]: case args[0]:
of "tcp-syni-connect": of "initiate":
let req = parseMessage[TcpSyniConnect](args[2]) case args[2]:
proc progress(seqNumbers: seq[uint32]) {.async.} = of "tcp-syni":
echo "progress! seqNumbers: ", seqNumbers let req = parseMessage[InitiateTcpSyni](args[3])
let content = @["tcp-syni-accept", $req.srcIp, req.srcPorts.join(","), proc progress(seqNumbers: seq[uint32]) {.async.} =
$req.dstIp, req.dstPorts.join(","), echo "progress! seqNumbers: ", seqNumbers
seqNumbers.join(",")].join("|") let content = @["tcp-syni", $req.srcIp, req.srcPorts.join(","),
await sendToClient(unixSock, &"progress|{id}|{content}\n") $req.dstIp, req.dstPorts.join(","),
sock = await punchd.tcpSyniCP.connect(req.srcPorts[0], req.dstIp, seqNumbers.join(",")].join("|")
req.dstPorts, progress) await sendToClient(unixSock, &"progress|{id}|{content}\n")
sock = await punchd.tcpSyniInitiator.initiate(req.srcPorts[0], req.dstIp,
req.dstPorts, progress)
of "tcp-syni-accept": of "tcp-nutss":
let req = parseMessage[TcpSyniAccept](args[2]) let req = parseMessage[InitiateTcpNutss](args[3])
sock = await punchd.tcpSyniAP.accept(req.srcPorts[0], req.dstIp, proc progress() {.async.} =
req.dstPorts, req.seqNums) echo "progress!"
let content = @["tcp-nutss", $req.srcIp, req.srcPorts.join(","),
$req.dstIp, req.dstPorts.join(",")].join("|")
await sendToClient(unixSock, &"progress|{id}|{content}\n")
sock = await punchd.tcpNutssInitiator.initiate(req.srcPorts[0], req.dstIp,
req.dstPorts, progress)
of "tcp-nutss-initiate": else:
let req = parseMessage[TcpNutssInitiate](args[2]) raise newException(ValueError, "invalid request")
proc progress() {.async.} =
echo "progress!"
let content = @["tcp-nutss-respond", $req.srcIp, req.srcPorts.join(","),
$req.dstIp, req.dstPorts.join(",")].join("|")
await sendToClient(unixSock, &"progress|{id}|{content}\n")
sock = await punchd.tcpNutssInitiator.initiate(req.srcPorts[0], req.dstIp,
req.dstPorts, progress)
of "tcp-nutss-respond":
let req = parseMessage[TcpNutssRespond](args[2]) of "respond":
sock = await punchd.tcpNutssResponder.respond(req.srcPorts[0], req.dstIp, case args[2]:
req.dstPorts) of "tcp-syni":
let req = parseMessage[RespondTcpSyni](args[3])
sock = await punchd.tcpSyniResponder.respond(req.srcPorts[0], req.dstIp,
req.dstPorts, req.seqNums)
of "tcp-nutss":
let req = parseMessage[RespondTcpNutss](args[3])
sock = await punchd.tcpNutssResponder.respond(req.srcPorts[0],
req.dstIp, req.dstPorts)
else:
raise newException(ValueError, "invalid request")
else: else:
raise newException(ValueError, "invalid request") raise newException(ValueError, "invalid request")
@ -134,8 +146,8 @@ proc main() =
{fpUserRead, fpUserWrite, fpGroupRead, fpGroupWrite, {fpUserRead, fpUserWrite, fpGroupRead, fpGroupWrite,
fpOthersRead, fpOthersWrite}) fpOthersRead, fpOthersWrite})
let punchd = Punchd(unixSocket: unixSocket, let punchd = Punchd(unixSocket: unixSocket,
tcpSyniCP: initTcpSyniConnectPuncher(), tcpSyniInitiator: initTcpSyniInitiator(),
tcpSyniAP: initTcpSyniAcceptPuncher(), tcpSyniResponder: initTcpSyniResponder(),
tcpNutssInitiator: initTcpNutssInitiator(), tcpNutssInitiator: initTcpNutssInitiator(),
tcpNutssResponder: initTcpNutssResponder()) tcpNutssResponder: initTcpNutssResponder())
asyncCheck handleUsers(punchd) asyncCheck handleUsers(punchd)
@ -143,8 +155,8 @@ proc main() =
runForever() runForever()
except Sigint: except Sigint:
waitFor punchd.tcpSyniCP.cleanup() waitFor punchd.tcpSyniInitiator.cleanup()
waitFor punchd.tcpSyniAP.cleanup() waitFor punchd.tcpSyniResponder.cleanup()
waitFor punchd.tcpNutssInitiator.cleanup() waitFor punchd.tcpNutssInitiator.cleanup()
waitFor punchd.tcpNutssResponder.cleanup() waitFor punchd.tcpNutssResponder.cleanup()
punchd.unixSocket.close() punchd.unixSocket.close()

View File

@ -17,19 +17,19 @@ type
dstIp: IpAddress dstIp: IpAddress
dstPorts: seq[Port] dstPorts: seq[Port]
TcpSyniConnectPuncher* = Puncher[Attempt] TcpSyniInitiator* = Puncher[Attempt]
PunchProgressCb* = proc(seqNums: seq[uint32]) {.async.} PunchProgressCb* = proc(seqNums: seq[uint32]) {.async.}
var IPPROTO_IP {.importc: "IPPROTO_IP", header: "<netinet/in.h>".}: cint var IPPROTO_IP {.importc: "IPPROTO_IP", header: "<netinet/in.h>".}: cint
var IP_TTL {.importc: "IP_TTL", header: "<netinet/in.h>".}: cint var IP_TTL {.importc: "IP_TTL", header: "<netinet/in.h>".}: cint
proc cleanup*(puncher: TcpSyniConnectPuncher) {.async.} = proc cleanup*(puncher: TcpSyniInitiator) {.async.} =
while puncher.attempts.len() != 0: while puncher.attempts.len() != 0:
await puncher.attempts.pop().deleteFirewallRules() await puncher.attempts.pop().deleteFirewallRules()
proc initTcpSyniConnectPuncher*(): TcpSyniConnectPuncher = proc initTcpSyniInitiator*(): TcpSyniInitiator =
TcpSyniConnectPuncher() TcpSyniInitiator()
proc captureSeqNumbers(attempt: Attempt, cb: PunchProgressCb) {.async.} = proc captureSeqNumbers(attempt: Attempt, cb: PunchProgressCb) {.async.} =
# FIXME: timeout? # FIXME: timeout?
@ -77,12 +77,12 @@ proc captureAndResendAck(attempt: Attempt) {.async.} =
closeSocket(captureFd) closeSocket(captureFd)
closeSocket(injectFd) closeSocket(injectFd)
proc doConnect(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress, dstPort: Port, proc connect(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress, dstPort: Port,
future: Future[AsyncSocket]) {.async.} = future: Future[AsyncSocket]) {.async.} =
let sock = newAsyncSocket() let sock = newAsyncSocket()
sock.setSockOpt(OptReuseAddr, true) sock.setSockOpt(OptReuseAddr, true)
sock.getFd.setSockOptInt(IPPROTO_IP, IP_TTL, 2) sock.getFd.setSockOptInt(IPPROTO_IP, IP_TTL, 2)
echo &"doConnect {srcIp}:{srcPort} -> {dstIp}:{dstPort}" echo &"connect {srcIp}:{srcPort} -> {dstIp}:{dstPort}"
sock.bindAddr(srcPort, $srcIp) sock.bindAddr(srcPort, $srcIp)
try: try:
await sock.connect($dstIp, dstPort) await sock.connect($dstIp, dstPort)
@ -92,9 +92,9 @@ proc doConnect(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress, dstPort: Port,
echo &"connection {srcIP}:{srcPort.int} -> {dstIp}:{dstPort.int} failed: ", e.msg echo &"connection {srcIP}:{srcPort.int} -> {dstIp}:{dstPort.int} failed: ", e.msg
sock.close() sock.close()
proc connect*(puncher: TcpSyniConnectPuncher, srcPort: Port, dstIp: IpAddress, proc initiate*(puncher: TcpSyniInitiator, srcPort: Port, dstIp: IpAddress,
dstPorts: seq[Port], dstPorts: seq[Port],
progressCb: PunchProgressCb): Future[AsyncSocket] {.async.} = progressCb: PunchProgressCb): Future[AsyncSocket] {.async.} =
let localIp = getPrimaryIPAddr(dstIp) let localIp = getPrimaryIPAddr(dstIp)
if puncher.findAttempt(localIp, srcPort, dstIp, dstPorts) != -1: if puncher.findAttempt(localIp, srcPort, dstIp, dstPorts) != -1:
raise newException(PunchHoleError, "hole punching for given parameters already active") raise newException(PunchHoleError, "hole punching for given parameters already active")
@ -107,7 +107,7 @@ proc connect*(puncher: TcpSyniConnectPuncher, srcPort: Port, dstIp: IpAddress,
try: try:
let connectFuture = newFuture[AsyncSocket]("connect") let connectFuture = newFuture[AsyncSocket]("connect")
for dstPort in attempt.dstPorts: for dstPort in attempt.dstPorts:
asyncCheck doConnect(attempt.srcIp, attempt.srcPort, attempt.dstIp, asyncCheck connect(attempt.srcIp, attempt.srcPort, attempt.dstIp,
dstPort, connectFuture) dstPort, connectFuture)
await connectFuture or sleepAsync(Timeout) await connectFuture or sleepAsync(Timeout)
await attempt.deleteFirewallRules() await attempt.deleteFirewallRules()

View File

@ -19,15 +19,15 @@ type
seqNums: seq[uint32] seqNums: seq[uint32]
future: Future[AsyncSocket] future: Future[AsyncSocket]
TcpSyniAcceptPuncher* = Puncher[Attempt] TcpSyniResponder* = Puncher[Attempt]
proc cleanup*(puncher: TcpSyniAcceptPuncher) {.async.} = proc cleanup*(puncher: TcpSyniResponder) {.async.} =
while puncher.attempts.len() != 0: while puncher.attempts.len() != 0:
await puncher.attempts.pop().deleteFirewallRules() await puncher.attempts.pop().deleteFirewallRules()
proc initTcpSyniAcceptPuncher*(): TcpSyniAcceptPuncher = proc initTcpSyniResponder*(): TcpSyniResponder =
randomize() randomize()
TcpSyniAcceptPuncher() TcpSyniResponder()
proc injectSynPackets(attempt: Attempt) {.async.} = proc injectSynPackets(attempt: Attempt) {.async.} =
let injectFd = setupTcpInjectingSocket() let injectFd = setupTcpInjectingSocket()
@ -50,8 +50,8 @@ proc injectSynPackets(attempt: Attempt) {.async.} =
await injectFd.injectTcpPacket(synIn) await injectFd.injectTcpPacket(synIn)
closeSocket(injectFd) closeSocket(injectFd)
proc doAccept(puncher: TcpSyniAcceptPuncher, srcIp: IpAddress, proc accept(puncher: TcpSyniResponder, srcIp: IpAddress,
srcPort: Port) {.async.} = srcPort: Port) {.async.} =
let sock = newAsyncSocket() let sock = newAsyncSocket()
sock.setSockOpt(OptReuseAddr, true) sock.setSockOpt(OptReuseAddr, true)
sock.bindAddr(srcPort, $srcIp) sock.bindAddr(srcPort, $srcIp)
@ -77,14 +77,14 @@ proc doAccept(puncher: TcpSyniAcceptPuncher, srcIp: IpAddress,
break break
sock.close() sock.close()
proc accept*(puncher: TcpSyniAcceptPuncher, srcPort: Port, dstIp: IpAddress, proc respond*(puncher: TcpSyniResponder, srcPort: Port, dstIp: IpAddress,
dstPorts: seq[Port], dstPorts: seq[Port],
seqNums: seq[uint32]): Future[AsyncSocket] {.async.} = seqNums: seq[uint32]): Future[AsyncSocket] {.async.} =
let localIp = getPrimaryIPAddr(dstIp) let localIp = getPrimaryIPAddr(dstIp)
let existingAttempts = puncher.findAttemptsByLocalAddr(localIp, srcPort) let existingAttempts = puncher.findAttemptsByLocalAddr(localIp, srcPort)
if existingAttempts.len() == 0: if existingAttempts.len() == 0:
echo &"accepting connections from {dstIp}:{dstPorts[0].int}" echo &"accepting connections from {dstIp}:{dstPorts[0].int}"
asyncCheck puncher.doAccept(localIp, srcPort) asyncCheck puncher.accept(localIp, srcPort)
else: else:
for a in existingAttempts: for a in existingAttempts:
if a.dstIp == dstIp and if a.dstIp == dstIp and