import asyncdispatch, asyncnet, os, strformat, strutils from nativesockets import Domain, SockType, Protocol from net import IpAddress, Port, `$` import asyncutils import message import tcp_syni from strutils import format, join from nativesockets import setSockOptInt const PunchdSocket = "/tmp/punchd.socket" type Punchd = ref object unixSocket: AsyncSocket tcpSyniPuncher: TcpSyniPuncher Sigint = object of CatchableError # Requests TcpSyniConnect = object srcIp: IpAddress srcPorts: seq[Port] dstIp: IpAddress dstPorts: seq[Port] TcpSyniAccept = object dstIp: IpAddress dstPorts: seq[Port] srcIp: IpAddress srcPorts: seq[Port] seqNums: seq[uint32] proc handleSigint() {.noconv.} = raise newException(Sigint, "received SIGINT") proc handleRequest(punchd: Punchd, line: string, unixSock: AsyncSocket) {.async.} = var id: string var sock: AsyncSocket try: let args = line.parseArgs(3) id = args[1] case args[0]: of "tcp-syni-connect": let req = parseMessage[TcpSyniConnect](args[2]) proc handleSeqNumbers(seqNumbers: seq[uint32]) {.async.} = echo "progress! seqNumbers: ", seqNumbers let content = @["tcp-syni-accept", $req.srcIp, req.srcPorts.join(","), $req.dstIp, req.dstPorts.join(","), seqNumbers.join(",")].join("|") await unixSock.send(&"progress|{id}|{content}\n") sock = await punchd.tcpSyniPuncher.connect(req.srcPorts[0], req.dstIp, req.dstPorts, handleSeqNumbers) of "tcp-syni-accept": let req = parseMessage[TcpSyniAccept](args[2]) sock = await punchd.tcpSyniPuncher.accept(req.srcPorts[0], req.dstIp, req.dstPorts, req.seqNums) else: raise newException(ValueError, "invalid request") let unixFd = unixSock.getFd.AsyncFD await unixFd.asyncSendMsg(&"ok|{id}\n", @[fromFd(sock.getFd.AsyncFD)]) except PunchHoleError as e: await unixSock.send(&"error|{id}|{e.msg}\n") except ValueError: unixSock.close proc handleRequests(punchd: Punchd, userSock: AsyncSocket) {.async.} = while true: if userSock.isClosed: break let line = await userSock.recvLine(maxLength = 400) if line.len == 0: break asyncCheck punchd.handleRequest(line, userSock) proc handleUsers(punchd: Punchd) {.async.} = while true: let user = await punchd.unixSocket.accept() asyncCheck punchd.handleRequests(user) proc main() = setControlCHook(handleSigint) removeFile(PunchdSocket) let unixSocket = newAsyncSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) unixSocket.bindUnix(PunchdSocket) unixSocket.listen() setFilePermissions(PunchdSocket, {fpUserRead, fpUserWrite, fpGroupRead, fpGroupWrite, fpOthersRead, fpOthersWrite}) let punchd = Punchd(unixSocket: unixSocket, tcpSyniPuncher: initPuncher()) asyncCheck handleUsers(punchd) try: runForever() except Sigint: waitFor punchd.tcpSyniPuncher.cleanup() punchd.unixSocket.close() removeFile(PunchdSocket) quit(0) when isMainModule: main()