package proxy import ( "crypto/rand" "net/http" "net/netip" "slices" ) // peerAddress is the address of the request's TCP peer. func peerAddress(r *http.Request) netip.Addr { addrPort, err := netip.ParseAddrPort(r.RemoteAddr) if err != nil { return netip.Addr{} } return addrPort.Addr().Unmap() } // requestIDHeader carries the request's id, from rproxy. const requestIDHeader = "X-Request-ID" // requestID is the request's id: the one a question gives, or for any other // request a new random one. func requestID(r *http.Request, isQuestion bool) string { if isQuestion { return r.Header.Get(requestIDHeader) } return rand.Text() } // scheme is how the visitor reached rproxy, or the trusted proxy in front of // it, as a question says in X-Forwarded-Proto, or otherwise http. func scheme(r *http.Request, isQuestion bool) string { proto := r.Header.Get("X-Forwarded-Proto") if !isQuestion || proto == "" { return "http" } return proto } // clientGroup is the client a request is counted toward: its IPv4 // address, or its IPv6 group, the netblock its IPv6 address is in of the // length SWWAF_IPV6_GROUP_PREFIX sets, a /64 by default, since one abuser // usually holds a whole /64. An IPv4 address in IPv6 form counts as IPv4. func (h *handler) clientGroup(addr netip.Addr) netip.Prefix { addr = addr.Unmap() if addr.Is6() { return netip.PrefixFrom(addr, h.config.IPv6GroupPrefix).Masked() } return netip.PrefixFrom(addr, addr.BitLen()) } // isInside reports whether addr is in one of the netblocks. func isInside(addr netip.Addr, netblocks []netip.Prefix) bool { return slices.ContainsFunc(netblocks, func(netblock netip.Prefix) bool { return netblock.Contains(addr) }) }