182 lines
3.9 KiB
Go
182 lines
3.9 KiB
Go
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
|
|
}
|