package gokrazy import ( "log" "net" "net/http" "strings" "sync" ) var ( privateNets []net.IPNet ipv6LinkLocal net.IPNet gokrazyGdns = func() *net.IPNet { _, net, err := net.ParseCIDR("fdf5:3606:2a21::/48") if err != nil { log.Panic(err) } return net }() ) // PrivateNetworks contains the CIDR representation of all networks which // gokrazy considers private. var PrivateNetworks = []string{ // loopback: https://tools.ietf.org/html/rfc3330#section-2 "127.0.0.0/8", // loopback: https://tools.ietf.org/html/rfc3513#section-2.4 "::1/128", // reserved: https://tools.ietf.org/html/rfc1918#section-3 "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", // reserved: https://tools.ietf.org/html/rfc4193#section-3.1 "fc00::/7", // link-local: https://tools.ietf.org/html/rfc3927#section-1.2 "169.254.0.0/16", // link-local: https://tools.ietf.org/html/rfc4291#section-2.4 "fe80::/10", } func init() { privateNets = make([]net.IPNet, len(PrivateNetworks)) for idx, s := range PrivateNetworks { _, net, err := net.ParseCIDR(s) if err != nil { log.Panic(err.Error()) } privateNets[idx] = *net if s == "fe80::/10" { ipv6LinkLocal = *net } } } // IsInPrivateNet reports whether ip is in PrivateNetworks. func IsInPrivateNet(ip net.IP) bool { return isPrivate("", ip) } func isPrivate(iface string, ipaddr net.IP) bool { if strings.HasPrefix(iface, "uplink") { return false } for _, n := range privateNets { if n.Contains(ipaddr) { return true } } return false } func interfaceAddrs(keep func(string, net.IP) bool) ([]string, error) { ifaces, err := net.Interfaces() if err != nil { return nil, err } var hosts []string for _, i := range ifaces { if i.Flags&net.FlagUp != net.FlagUp { continue } addrs, err := i.Addrs() if err != nil { return nil, err } for _, a := range addrs { ipaddr, _, err := net.ParseCIDR(a.String()) if err != nil { return nil, err } if gokrazyGdns.Contains(ipaddr) { continue } if !keep(i.Name, ipaddr) { continue } host := ipaddr.String() if ipv6LinkLocal.Contains(ipaddr) { host = host + "%" + i.Name } hosts = append(hosts, host) } } return hosts, nil } // PrivateInterfaceAddrs returns all private (as per RFC1918, RFC4193, // RFC3330, RFC3513, RFC3927, RFC4291) host addresses of all active // interfaces, suitable to be passed to net.JoinHostPort. func PrivateInterfaceAddrs() ([]string, error) { return interfaceAddrs(isPrivate) } // PublicInterfaceAddrs returns all public (excluding RFC1918, RFC4193, // RFC3330, RFC3513, RFC3927, RFC4291) host addresses of all active // interfaces, suitable to be passed to net.JoinHostPort. func PublicInterfaceAddrs() ([]string, error) { return interfaceAddrs(func(iface string, addr net.IP) bool { return !isPrivate(iface, addr) }) } var ( listeners = make(map[string]*http.Server) listenersMu sync.Mutex ) func updateListeners(port string) error { hosts, err := PrivateInterfaceAddrs() if err != nil { return err } listenersMu.Lock() defer listenersMu.Unlock() vanished := make(map[string]bool) for host := range listeners { vanished[host] = false } for _, host := range hosts { if _, ok := listeners[host]; ok { // confirm found delete(vanished, host) continue } addr := net.JoinHostPort(host, port) ln, err := net.Listen("tcp", addr) if err != nil { log.Println(err) // err includes enough context continue } log.Printf("now listening on %s", addr) // add a new listener srv := &http.Server{ Handler: http.HandlerFunc(authenticated), } listeners[host] = srv go func(host string, srv *http.Server) { err := srv.Serve(ln) log.Printf("serving on %s: %v", addr, err) listenersMu.Lock() defer listenersMu.Unlock() delete(listeners, host) }(host, srv) } for host := range vanished { log.Printf("no longer listening on %s", net.JoinHostPort(host, port)) listeners[host].Close() delete(listeners, host) } return nil }