221 lines
4.9 KiB
Go
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
|
|
}
|