gokrazy/listeners.go
2021-01-08 09:10:39 +01:00

221 lines
4.9 KiB
Go

package gokrazy
import (
"crypto/tls"
"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)
})
}
// Wrapper to keep track of allocated servers per host
type krazyServer struct {
srv *http.Server
port string
}
var (
listeners = make(map[string][]*krazyServer)
listenersMu sync.Mutex
)
// tlsConfig: tlsConfig. nil, if the listeners should not use https (e.g. for redirects)
func updateListeners(port, redirectPort string, tlsEnabled bool, tlsConfig *tls.Config) 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 servers, ok := listeners[host]; ok {
// confirm found
delete(vanished, host)
cont := false
for _, server := range servers {
if server.port == port {
cont = true
break
}
}
if cont {
continue
}
}
addr := net.JoinHostPort(host, port)
ln, err := net.Listen("tcp", addr)
if err != nil {
log.Printf("listen: %v", err)
continue
}
log.Printf("now listening on %s", addr)
// add a new listener
var srv *http.Server
if tlsEnabled && tlsConfig == nil {
// "Redirect" server
srv = &http.Server{
Handler: http.HandlerFunc(httpsRedirect(redirectPort)),
TLSConfig: tlsConfig,
}
} else {
// "Content" server
srv = &http.Server{
Handler: http.HandlerFunc(authenticated),
TLSConfig: tlsConfig,
}
}
if _, ok := listeners[host]; ok {
listeners[host] = append(listeners[host], &krazyServer{srv, port})
} else {
listeners[host] = []*krazyServer{&krazyServer{srv, port}}
}
go func(host string, srv *http.Server) {
var err error
if tlsEnabled && tlsConfig != nil {
err = srv.ServeTLS(ln, "", "")
} else {
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))
for _, server := range listeners[host] {
server.srv.Close()
}
delete(listeners, host)
}
return nil
}