138 lines
2.8 KiB
Go
138 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type Service struct {
|
|
Name string
|
|
Subdomains map[string]bool
|
|
Directories map[string]bool
|
|
FilterV4 bool
|
|
FilterV6 bool
|
|
}
|
|
|
|
func BuildServices(cfg *Config) map[string]*Service {
|
|
services := make(map[string]*Service)
|
|
|
|
for name, sc := range cfg.Services {
|
|
if !sc.Enabled {
|
|
continue
|
|
}
|
|
svc := &Service{
|
|
Name: name,
|
|
Subdomains: make(map[string]bool),
|
|
Directories: make(map[string]bool),
|
|
}
|
|
for _, s := range sc.Subdomains {
|
|
svc.Subdomains[strings.ToLower(s)] = true
|
|
}
|
|
for _, d := range sc.Directories {
|
|
svc.Directories[d] = true
|
|
}
|
|
switch name {
|
|
case "ip4":
|
|
svc.FilterV4 = true
|
|
case "ip6":
|
|
svc.FilterV6 = true
|
|
}
|
|
services[name] = svc
|
|
}
|
|
return services
|
|
}
|
|
|
|
func extractIP(r *http.Request) net.IP {
|
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
|
parts := strings.Split(xff, ",")
|
|
if ip := net.ParseIP(strings.TrimSpace(parts[0])); ip != nil {
|
|
return ip
|
|
}
|
|
}
|
|
if xri := r.Header.Get("X-Real-IP"); xri != "" {
|
|
if ip := net.ParseIP(strings.TrimSpace(xri)); ip != nil {
|
|
return ip
|
|
}
|
|
}
|
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
return net.ParseIP(r.RemoteAddr)
|
|
}
|
|
return net.ParseIP(host)
|
|
}
|
|
|
|
func matchService(host, path string, services map[string]*Service) *Service {
|
|
hostname := host
|
|
if i := strings.IndexByte(host, ':'); i != -1 {
|
|
hostname = host[:i]
|
|
}
|
|
|
|
for _, svc := range services {
|
|
if svc.Subdomains[hostname] {
|
|
return svc
|
|
}
|
|
}
|
|
|
|
parts := strings.Split(hostname, ".")
|
|
if len(parts) >= 2 {
|
|
label := parts[len(parts)-2]
|
|
for _, svc := range services {
|
|
if svc.Subdomains[label] {
|
|
return svc
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, svc := range services {
|
|
if svc.Directories[path] {
|
|
return svc
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Handle(services map[string]*Service, geo *Geo) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
svc := matchService(r.Host, r.URL.Path, services)
|
|
if svc == nil {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
ip := extractIP(r)
|
|
if ip == nil {
|
|
http.Error(w, "cannot determine client IP", 500)
|
|
return
|
|
}
|
|
|
|
isV4 := ip.To4() != nil
|
|
if (svc.FilterV4 && !isV4) || (svc.FilterV6 && isV4) {
|
|
http.Error(w, fmt.Sprintf("this endpoint requires %s", map[bool]string{true: "IPv6", false: "IPv4"}[svc.FilterV6]), 400)
|
|
return
|
|
}
|
|
|
|
info := geo.Lookup(ip)
|
|
|
|
fmt.Fprintf(w, "%s\n", ip)
|
|
fmt.Fprintf(w, "%s\n", info.ReverseDNS)
|
|
fmt.Fprintf(w, "proxy=NO, type=UNKNOWN, tor=NO\n")
|
|
continent, country, city := info.Continent, info.Country, info.City
|
|
if continent == "" {
|
|
continent = "Unknown"
|
|
}
|
|
if country == "" {
|
|
country = "Unknown"
|
|
}
|
|
if city == "" {
|
|
city = "Unknown"
|
|
}
|
|
fmt.Fprintf(w, "%s, %s, %s\n", continent, country, city)
|
|
if info.Org != "" {
|
|
fmt.Fprintf(w, "%s / %s\n", info.Org, info.ASN)
|
|
} else {
|
|
fmt.Fprintf(w, "Unknown\n")
|
|
}
|
|
}
|
|
}
|