diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..49468ed --- /dev/null +++ b/.air.toml @@ -0,0 +1,46 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "./tmp/main" + cmd = "go build -o ./tmp/main ." + delay = 1000 + exclude_dir = ["assets", "tmp", "vendor", "testdata"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/.gitignore b/.gitignore index adf8f72..c5d8c1b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ # Go workspace file go.work + +tmp diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5ef61d9 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.mzsl.nl/mans/ip-svc + +go 1.22.3 diff --git a/main.go b/main.go new file mode 100644 index 0000000..32925cb --- /dev/null +++ b/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + "net" + "net/http" +) + +type AddressType string + +const ( + IPv4 AddressType = "IPv4" + IPv6 AddressType = "IPv6" +) + +type NetAddress struct { + Address string `json:"address"` + Type AddressType `json:"type"` +} + +type contextKey string + +const netAddrKey = contextKey("netAddr") + +func RealIpMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + netAddr := extractNetAddress(r) + if netAddr.Address == "" { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + ctx := context.WithValue(r.Context(), netAddrKey, netAddr) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +func extractNetAddress(r *http.Request) NetAddress { + realIpHeader := r.Header.Get("X-Real-Ip") + if realIpHeader != "" { + if ip := net.ParseIP(realIpHeader); ip != nil { + return NetAddress{ + Address: realIpHeader, + Type: determineIPType(ip), + } + } + log.Printf("Invalid X-Real-Ip header: %s", realIpHeader) + } + + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + log.Printf("Failed to split host and port: %v", err) + return NetAddress{} + } + + if ip := net.ParseIP(host); ip != nil { + return NetAddress{ + Address: host, + Type: determineIPType(ip), + } + } + return NetAddress{} +} + +func determineIPType(ip net.IP) AddressType { + if ip.To4() != nil { + return IPv4 + } + return IPv6 +} + +func mainHandler(w http.ResponseWriter, r *http.Request) { + netAddr, ok := r.Context().Value(netAddrKey).(NetAddress) + if !ok { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + switch r.Method { + case http.MethodGet: + switch r.URL.Path { + case "/": + fmt.Fprintf(w, "%s\n%s", netAddr.Address, netAddr.Type) + case "/api": + responseJSON(w, netAddr) + default: + http.NotFound(w, r) + } + default: + http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed) + } +} + +func responseJSON(w http.ResponseWriter, data interface{}) { + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(data) + if err != nil { + http.Error(w, "Failed to encode JSON", http.StatusInternalServerError) + } +} + +func main() { + httpHandler := http.HandlerFunc(mainHandler) + log.Println("Starting server on :8080") + if err := http.ListenAndServe(":8080", RealIpMiddleware(httpHandler)); err != nil { + log.Fatalf("Server failed: %v", err) + } +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..6d58ac4 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,12 @@ + + + + My IP + + + + + IPv4: asdf + IPv6: asdf + +