#lang racket/base ;; Network helpers used by UPnP and SSDP. (require racket/list racket/string racket/udp simple-log) (provide upnp-default-ipv4-address) (sl-def-log upnp-network) ;; These destinations are only used to make the operating system select its ;; normal outbound IPv4 route. UDP connect does not transmit a datagram. (define route-probes '(("1.1.1.1" 53) ("8.8.8.8" 53))) (define (loopback-ipv4-address? address) (string-prefix? address "127.")) (define (usable-ipv4-address? address) (and (string? address) (not (string=? address "0.0.0.0")) (not (loopback-ipv4-address? address)))) (define (local-address-for destination port) (let ([socket (udp-open-socket destination port)]) (dynamic-wind void (lambda () (udp-connect! socket destination port) (define-values (local-address local-port remote-address remote-port) (udp-addresses socket #t)) local-address) (lambda () (udp-close socket))))) ;; Return the local non-loopback IPv4 address selected by the operating system ;; for normal outbound traffic. This is also the interface normally suitable ;; for SSDP multicast. No datagram is transmitted. (define (upnp-default-ipv4-address) (or (for/or ([probe (in-list route-probes)]) (define destination (first probe)) (define port (second probe)) (with-handlers ([exn:fail? (lambda (exception) (dbg-upnp-network "Could not determine local IPv4 address using route to ~a:~a: ~a" destination port (exn-message exception)) #f)]) (define address (local-address-for destination port)) (cond [(usable-ipv4-address? address) (dbg-upnp-network "Selected UPnP IPv4 address ~a using route to ~a:~a" address destination port) address] [else (dbg-upnp-network "Rejected unusable local IPv4 address ~a selected for route to ~a:~a" address destination port) #f]))) (error 'upnp-default-ipv4-address "could not determine a non-loopback IPv4 address")))