diff --git a/internal/server/helpers.go b/internal/server/helpers.go index f2f7bbb..3a2baab 100644 --- a/internal/server/helpers.go +++ b/internal/server/helpers.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "log/slog" + "net" "net/http" "strings" "time" @@ -87,22 +88,23 @@ func (s *statusRecorder) WriteHeader(c int) { func clientIP(r *http.Request) string { if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" { if i := strings.IndexByte(fwd, ','); i > 0 { - return strings.TrimSpace(fwd[:i]) + return stripBrackets(strings.TrimSpace(fwd[:i])) } - return strings.TrimSpace(fwd) + return stripBrackets(strings.TrimSpace(fwd)) } - if host, _, ok := splitHostPort(r.RemoteAddr); ok { + if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { + // net.SplitHostPort returns IPv6 without brackets already. return host } - return r.RemoteAddr + return stripBrackets(r.RemoteAddr) } -// splitHostPort is a port-tolerant version of net.SplitHostPort that doesn't -// error on missing port. -func splitHostPort(s string) (string, string, bool) { - i := strings.LastIndexByte(s, ':') - if i < 0 { - return s, "", false +// stripBrackets removes the `[...]` wrapping IPv6 hosts pick up from +// net/http's RemoteAddr in some Go versions, since Postgres `inet` rejects +// `[::1]` but accepts `::1`. +func stripBrackets(s string) string { + if len(s) >= 2 && s[0] == '[' && s[len(s)-1] == ']' { + return s[1 : len(s)-1] } - return s[:i], s[i+1:], true + return s }