punchd/punchd.nim

105 lines
3.0 KiB
Nim

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