2020-07-06 15:10:10 +02:00
|
|
|
import asyncdispatch, asyncnet, os, strformat, strutils, tables
|
|
|
|
from net import IpAddress, Port, `$`
|
|
|
|
import ../../message
|
|
|
|
|
|
|
|
# TODO: we need to find out our own external IP and return it to clients who
|
|
|
|
# connect from our LAN
|
|
|
|
|
|
|
|
type
|
2020-07-07 19:34:34 +02:00
|
|
|
Client = object
|
|
|
|
sock: AsyncSocket
|
|
|
|
ip: IpAddress
|
|
|
|
ports: array[3, Port]
|
|
|
|
|
|
|
|
# Requests
|
2020-07-06 15:10:10 +02:00
|
|
|
Register = object
|
|
|
|
peerId: string
|
|
|
|
ip: IpAddress
|
|
|
|
ports: array[3, Port]
|
|
|
|
|
|
|
|
GetInfo = object
|
|
|
|
peerId: string
|
|
|
|
|
|
|
|
NotifyPeer = object
|
|
|
|
sender: string
|
|
|
|
recipient: string
|
|
|
|
data: string
|
|
|
|
|
|
|
|
proc removeClient(clients: TableRef[string, Client], peerId: string) =
|
|
|
|
if peerId.len > 0: clients.del(peerId)
|
|
|
|
|
|
|
|
proc processClient(client: AsyncSocket, clients: TableRef[string, Client]) {.async.} =
|
|
|
|
let (address, port) = client.getPeerAddr
|
|
|
|
await client.send(&"notify-endpoint|{address}|{port.int}\n")
|
|
|
|
var id = ""
|
|
|
|
var peerId = ""
|
|
|
|
while true:
|
2020-07-21 10:17:36 +02:00
|
|
|
var line = await client.recvLine(maxLength = 400)
|
|
|
|
line = line.strip(leading = false, trailing = true, chars = {'\r', '\n'})
|
2020-07-06 15:10:10 +02:00
|
|
|
if line.len == 0:
|
|
|
|
removeClient(clients, peerId)
|
|
|
|
break
|
|
|
|
try:
|
|
|
|
let args = line.parseArgs(3)
|
|
|
|
id = args[1]
|
|
|
|
case args[0]:
|
|
|
|
of "register":
|
|
|
|
let req = parseMessage[Register](args[2])
|
|
|
|
peerId = req.peerId
|
|
|
|
clients[peerId] = Client(sock: client, ip: req.ip, ports: req.ports)
|
|
|
|
asyncCheck client.send(&"ok|{id}\n")
|
|
|
|
of "get-info":
|
|
|
|
let req = parseMessage[GetInfo](args[2])
|
|
|
|
let peer = clients[req.peerId]
|
|
|
|
let peerPorts = peer.ports.join(",")
|
|
|
|
asyncCheck client.send(&"ok|{id}|{peer.ip}|{peerPorts}\n")
|
|
|
|
of "notify-peer":
|
|
|
|
let req = parseMessage[NotifyPeer](args[2])
|
|
|
|
let recipient = clients[req.recipient]
|
|
|
|
asyncCheck recipient.sock.send(&"notify-peer|{req.sender}|{req.recipient}|{req.data}\n")
|
|
|
|
asyncCheck client.send(&"ok|{id}\n")
|
|
|
|
else:
|
|
|
|
client.close()
|
|
|
|
removeClient(clients, peerId)
|
|
|
|
break
|
|
|
|
|
|
|
|
except KeyError:
|
|
|
|
asyncCheck client.send(&"error|{id}|peer not registered\n")
|
|
|
|
|
|
|
|
except ValueError:
|
|
|
|
client.close
|
|
|
|
removeClient(clients, peerId)
|
|
|
|
break
|
|
|
|
|
|
|
|
proc serve(port: Port) {.async.} =
|
|
|
|
var clients = newTable[string, Client]()
|
|
|
|
var server = newAsyncSocket()
|
|
|
|
server.setSockOpt(OptReuseAddr, true)
|
|
|
|
server.bindAddr(port)
|
|
|
|
server.listen()
|
|
|
|
|
|
|
|
while true:
|
|
|
|
let client = await server.accept()
|
|
|
|
asyncCheck processClient(client, clients)
|
|
|
|
|
|
|
|
proc main() =
|
|
|
|
if paramCount() != 1:
|
|
|
|
echo(fmt"usage: {paramStr(0)} PORT")
|
|
|
|
quit(1)
|
|
|
|
|
|
|
|
try:
|
|
|
|
let portNumber = paramStr(1).parseUInt
|
|
|
|
if portNumber > uint16.high:
|
|
|
|
raise newException(ValueError, "port out of range")
|
|
|
|
let port = Port(portNumber)
|
|
|
|
asyncCheck serve(port)
|
|
|
|
runForever()
|
|
|
|
|
|
|
|
except ValueError as e:
|
|
|
|
echo e.msg
|
|
|
|
|
|
|
|
when isMainModule:
|
|
|
|
main()
|