mirror of
https://github.com/cunnie/sslip.io.git
synced 2025-10-07 00:23:44 +08:00
wildcard-dns-http-server: better error-checking
- when DNS gets a permission error, it helpfully suggests using `sudo` - when DNS can't bind to `INADDR_ANY`, it's probably because it's Fedora running `systemd.resolved` on port 53 of 127.0.0.53, so we try to bind to each address individually. - we don't implement similar checks for the HTTP server: - if it's a permission problem, the DNS server has already warned the user. - if it's a binding problem, the user is probably running an HTTP server bound to `INADDR_ANY`, so we might as well exit. - we ported this code from main `sslip.io` DNS server.
This commit is contained in:
@@ -8,7 +8,11 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
)
|
)
|
||||||
@@ -21,24 +25,58 @@ type Txt struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
log.Println("DNS: starting up.")
|
||||||
conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})
|
conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})
|
||||||
if err != nil {
|
switch {
|
||||||
log.Fatal(err.Error())
|
case err == nil:
|
||||||
|
log.Println(`DNS: Successfully bound to all interfaces, port 53.`)
|
||||||
|
wg.Add(1)
|
||||||
|
go dnsServer(conn, &wg)
|
||||||
|
case isErrorPermissionsError(err):
|
||||||
|
log.Println("DNS: Try invoking me with `sudo` because I don't have permission to bind to port 53.")
|
||||||
|
log.Fatal("DNS: " + err.Error())
|
||||||
|
case isErrorAddressAlreadyInUse(err):
|
||||||
|
log.Println(`DNS: I couldn't bind to "0.0.0.0:53" (INADDR_ANY, all interfaces), so I'll try to bind to each address individually.`)
|
||||||
|
ipCIDRs := listLocalIPCIDRs()
|
||||||
|
var boundIPsPorts, unboundIPs []string
|
||||||
|
for _, ipCIDR := range ipCIDRs {
|
||||||
|
ip, _, err := net.ParseCIDR(ipCIDR)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf(`DNS: I couldn't parse the local interface "%s".`, ipCIDR)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conn, err = net.ListenUDP("udp", &net.UDPAddr{
|
||||||
|
IP: ip,
|
||||||
|
Port: 53,
|
||||||
|
Zone: "",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
unboundIPs = append(unboundIPs, ip.String())
|
||||||
|
} else {
|
||||||
|
wg.Add(1)
|
||||||
|
boundIPsPorts = append(boundIPsPorts, conn.LocalAddr().String())
|
||||||
|
go dnsServer(conn, &wg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(boundIPsPorts) > 0 {
|
||||||
|
log.Printf(`DNS: I bound to the following: "%s"`, strings.Join(boundIPsPorts, `", "`))
|
||||||
|
}
|
||||||
|
if len(unboundIPs) > 0 {
|
||||||
|
log.Printf(`DNS: I couldn't bind to the following IPs: "%s"`, strings.Join(unboundIPs, `", "`))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Fatal("DNS: " + err.Error())
|
||||||
}
|
}
|
||||||
|
wg.Add(1)
|
||||||
var group sync.WaitGroup
|
go httpServer(&wg)
|
||||||
group.Add(1)
|
wg.Wait()
|
||||||
go dnsServer(conn, &group)
|
|
||||||
group.Add(1)
|
|
||||||
go httpServer(&group)
|
|
||||||
group.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func dnsServer(conn *net.UDPConn, group *sync.WaitGroup) {
|
func dnsServer(conn *net.UDPConn, group *sync.WaitGroup) {
|
||||||
var query dnsmessage.Message
|
var query dnsmessage.Message
|
||||||
|
|
||||||
defer group.Done()
|
defer group.Done()
|
||||||
log.Println("DNS: starting up.")
|
|
||||||
queryRaw := make([]byte, 512)
|
queryRaw := make([]byte, 512)
|
||||||
for {
|
for {
|
||||||
_, addr, err := conn.ReadFromUDP(queryRaw)
|
_, addr, err := conn.ReadFromUDP(queryRaw)
|
||||||
@@ -111,6 +149,7 @@ func usageHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
log.Println("HTTP: " + err.Error())
|
log.Println("HTTP: " + err.Error())
|
||||||
}
|
}
|
||||||
|
log.Printf("HTTP: wrong path (%s) with method (%s).\n", r.URL.Path, r.Method)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTxtHandler(w http.ResponseWriter, r *http.Request) {
|
func updateTxtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -143,7 +182,55 @@ func updateTxtHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
log.Println("HTTP: " + err.Error())
|
log.Println("HTTP: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("Creating new TXT record \"" + updateTxt.Txt + "\".")
|
log.Println("HTTP: Creating new TXT record \"" + updateTxt.Txt + "\".")
|
||||||
// this is the money shot, where we create a new DNS TXT record to what was in the POST request
|
// this is the money shot, where we create a new DNS TXT record to what was in the POST request
|
||||||
txts = append(txts, updateTxt.Txt)
|
txts = append(txts, updateTxt.Txt)
|
||||||
}
|
}
|
||||||
|
func listLocalIPCIDRs() []string {
|
||||||
|
var ifaces []net.Interface
|
||||||
|
var cidrStrings []string
|
||||||
|
var err error
|
||||||
|
if ifaces, err = net.Interfaces(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
var cidrs []net.Addr
|
||||||
|
if cidrs, err = iface.Addrs(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, cidr := range cidrs {
|
||||||
|
cidrStrings = append(cidrStrings, cidr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cidrStrings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thanks https://stackoverflow.com/a/52152912/2510873
|
||||||
|
func isErrorAddressAlreadyInUse(err error) bool {
|
||||||
|
var eOsSyscall *os.SyscallError
|
||||||
|
if !errors.As(err, &eOsSyscall) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr)
|
||||||
|
if !errors.As(eOsSyscall, &errErrno) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if errErrno == syscall.EADDRINUSE {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const WSAEADDRINUSE = 10048
|
||||||
|
if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isErrorPermissionsError(err error) bool {
|
||||||
|
var eOsSyscall *os.SyscallError
|
||||||
|
if errors.As(err, &eOsSyscall) {
|
||||||
|
if os.IsPermission(eOsSyscall) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user