import asyncdispatch, asyncnet, os, strformat, strutils from nativesockets import Domain, SockType, Protocol import asyncutils import message import tables import tcp_syni_initiator import tcp_syni_responder import tcp_nutss_initiator import tcp_nutss_responder from strutils import format, join from nativesockets import setSockOptInt type Punchd = ref object unixSocket: AsyncSocket initiators: Table[string, Initiator] responders: Table[string, Responder] Sigint = object of CatchableError const PunchdSocket = "/tmp/punchd.socket" proc handleSigint() {.noconv.} = raise newException(Sigint, "received SIGINT") proc sendToClient(unixSock: AsyncSocket, msg: string, cmsgs: seq[ControlMessage] = @[]) {.async.} = if not unixSock.isClosed(): let unixFd = unixSock.getFd.AsyncFD await unixFd.asyncSendMsg(msg, cmsgs) proc handleRequest(punchd: Punchd, line: string, unixSock: AsyncSocket) {.async.} = var id: string var sock: AsyncSocket try: let args = line.parseArgs(4) id = args[1] case args[0]: of "initiate": proc progress(extraArgs: string) {.async.} = let msg = &"progress|{id}|{args[2]}|{args[3]}|{extraArgs}\n" await sendToClient(unixSock, msg) sock = await punchd.initiators[args[2]].initiate(args[3], progress) of "respond": sock = await punchd.responders[args[2]].respond(args[3]) else: raise newException(ValueError, "invalid request") await sendToClient(unixSock, &"ok|{id}\n", @[fromFd(sock.getFd.AsyncFD)]) sock.close() except PunchHoleError as e: await sendToClient(unixSock, &"error|{id}|{e.msg}\n") except KeyError, 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: userSock.close() 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) punchd.initiators["tcp-syni"] = initTcpSyniInitiator() punchd.initiators["tcp-nutss"] = initTcpNutssInitiator() punchd.responders["tcp-syni"] = initTcpSyniResponder() punchd.responders["tcp-nutss"] = initTcpNutssResponder() asyncCheck handleUsers(punchd) try: runForever() except Sigint: for i in punchd.initiators.values: waitFor i.cleanup() for r in punchd.responders.values: waitFor r.cleanup() punchd.unixSocket.close() removeFile(PunchdSocket) when isMainModule: main()