implement primitive probing of the rendezvous server's public IP and provide it to LAN clients

This commit is contained in:
Christian Ulrich 2020-07-22 02:04:02 +02:00
parent 32ecd8074c
commit 4f64b7be53
No known key found for this signature in database
GPG Key ID: 8241BE099775A097
1 changed files with 45 additions and 8 deletions

View File

@ -1,9 +1,9 @@
import asyncdispatch, asyncnet, os, strformat, strutils, tables import asyncdispatch, asyncnet, os, sequtils, strformat, strutils, tables
from net import IpAddress, Port, `$` from nativesockets import htonl
from net import IpAddress, Port, getPrimaryIPAddr, parseIpAddress, `$`
import ../../asyncutils
import ../../message import ../../message
import ../../network_interface
# TODO: we need to find out our own external IP and return it to clients who
# connect from our LAN
type type
Client = object Client = object
@ -25,12 +25,49 @@ type
recipient: string recipient: string
data: string data: string
proc isPrivateIp(ip: IpAddress): bool =
const ranges: array[5, tuple[first: IpAddress, last: IpAddress]] =
[(parseIpAddress("10.0.0.0"), parseIpAddress("10.255.255.255")),
(parseIpAddress("172.16.0.0"), parseIpAddress("172.31.255.255")),
(parseIpAddress("192.168.0.0"), parseIpAddress("192.168.255.255")),
(parseIpAddress("169.254.0.0"), parseIpAddress("169.254.255.255")),
(parseIpAddress("127.0.0.0"), parseIpAddress("127.255.255.255"))]
for r in ranges:
let ipScalar = htonl(cast[uint32](ip.address_v4))
if ipScalar > htonl(cast[uint32](r.first.address_v4)) and
ipScalar < htonl(cast[uint32](r.last.address_v4)):
return true
return false
proc isInNetwork(ip: IpAddress, iface: NetworkInterface): bool =
let ipScalar = htonl(cast[uint32](ip.address_v4))
let ifaceIpScalar = htonl(cast[uint32](iface.ipAddress.address_v4))
let netmaskScalar = htonl(cast[uint32](iface.netmask.address_v4))
(ipScalar and netmaskScalar) == (ifaceIpScalar and netmaskScalar)
proc probePublicIp(): Future[IpAddress] {.async.} =
let output = await asyncExecCmd("ping -R -c 1 -s 1 -n 193.0.14.129")
let ipLines = output.splitLines()
.filter(proc(l: string): bool = l.startsWith("\t") or l.startsWith("RR:"))
.map(proc(l: string): string = l.strip(true, false, {'R', ':', '\t', ' '}))
for line in ipLines:
let ipAddr = parseIpAddress(line)
if not isPrivateIp(ipAddr):
return ipAddr
block:
raise newException(OSError, "cannot probe public IP address")
proc removeClient(clients: TableRef[string, Client], peerId: string) = proc removeClient(clients: TableRef[string, Client], peerId: string) =
if peerId.len > 0: clients.del(peerId) if peerId.len > 0: clients.del(peerId)
proc processClient(client: AsyncSocket, clients: TableRef[string, Client]) {.async.} = proc processClient(client: AsyncSocket,
let (address, port) = client.getPeerAddr clients: TableRef[string, Client]) {.async.} =
await client.send(&"notify-endpoint|{address}|{port.int}\n") let (address, port) = client.getPeerAddr()
var ipAddr = parseIpAddress(address)
if ipAddr.isPrivateIp() and
ipAddr.isInNetwork(fromIpAddress(getPrimaryIPAddr(ipAddr))):
ipAddr = await probePublicIp()
await client.send(&"notify-endpoint|{ipAddr}|{port.int}\n")
var id = "" var id = ""
var peerId = "" var peerId = ""
while true: while true: