diff --git a/examples/rendezvous_server/rendezvous_server.nim b/examples/rendezvous_server/rendezvous_server.nim index 4e56def..d4fa1ab 100644 --- a/examples/rendezvous_server/rendezvous_server.nim +++ b/examples/rendezvous_server/rendezvous_server.nim @@ -1,9 +1,9 @@ -import asyncdispatch, asyncnet, os, strformat, strutils, tables -from net import IpAddress, Port, `$` +import asyncdispatch, asyncnet, os, sequtils, strformat, strutils, tables +from nativesockets import htonl +from net import IpAddress, Port, getPrimaryIPAddr, parseIpAddress, `$` +import ../../asyncutils import ../../message - -# TODO: we need to find out our own external IP and return it to clients who -# connect from our LAN +import ../../network_interface type Client = object @@ -25,12 +25,49 @@ type recipient: 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) = 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") +proc processClient(client: AsyncSocket, + clients: TableRef[string, Client]) {.async.} = + 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 peerId = "" while true: