check / check (push) Waiting to run
The package passes nothing to an app since rproxy took that over, so it is named after the command it serves. Its constructor is New, like rproxy's. The rproxy tests call their stand-in for servewaf standIn, since a local servewaf would hide the package. No change of behaviour. Model: opus-5-5
79 lines
2.6 KiB
Go
79 lines
2.6 KiB
Go
package servewaf
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/netip"
|
|
"time"
|
|
|
|
"sneak.berlin/go/smallwebwaf/internal/lookup"
|
|
)
|
|
|
|
// The headers in which an answer of allow gives rproxy the client's AS number
|
|
// and country, for the app, while SWWAF_ADD_LOOKUP_HEADERS is set. Go writes
|
|
// every header name in this form, as it sends it and as it receives it, so
|
|
// X-Client-ASN arrives as X-Client-Asn; header names are not case-sensitive.
|
|
const (
|
|
asnHeader = "X-Client-Asn"
|
|
countryHeader = "X-Client-Country"
|
|
)
|
|
|
|
// lookUp looks up the client's AS number and country, in the lookup
|
|
// database or through GeoJS, and notes them for the log line, unless
|
|
// SWWAF_LOOKUP_SOURCE is off or the client is on a private, loopback or
|
|
// link-local address, which no lookup can place. The lookup database
|
|
// answers at once. With GeoJS, while a setting needs the answer, such as a
|
|
// country list or a biased threshold, a new client's request waits for it,
|
|
// unless refusedWhateverTheAnswer reports true. ctx is the request's own
|
|
// context.
|
|
func (rq *request) lookUp(ctx context.Context, now time.Time) {
|
|
if rq.h.config.LookupSource == "off" || !canBePlaced(rq.client) {
|
|
return
|
|
}
|
|
|
|
client := rq.h.clientGroup(rq.client)
|
|
|
|
switch {
|
|
case rq.h.config.LookupSource == "file":
|
|
rq.lookupAnswer = rq.h.lookupFile.LookUp(client)
|
|
case rq.refusedWhateverTheAnswer(now):
|
|
rq.lookupAnswer = rq.h.geojs.LookUpWithoutWaiting(ctx, client)
|
|
rq.skippedLookupWait = rq.lookupAnswer.Answered.IsZero()
|
|
default:
|
|
rq.lookupAnswer = rq.h.geojs.LookUp(ctx, client)
|
|
}
|
|
|
|
rq.lookedUp = true
|
|
rq.line.ASN = rq.lookupAnswer.ASN
|
|
rq.line.ASName = rq.lookupAnswer.ASName
|
|
rq.line.Country = rq.lookupAnswer.Country
|
|
}
|
|
|
|
// addLookup adds answer, an answer about a client from the lookup
|
|
// database or GeoJS, to the client's history, and to the notes of the bans
|
|
// on its netblock that have no AS number, AS name or country yet.
|
|
func (h *handler) addLookup(answer lookup.Answer) {
|
|
h.limiter.AddLookup(answer.Client, answer.Answered,
|
|
answer.ASN, answer.ASName, answer.Country)
|
|
h.ledger.AddLookup(h.netblock(answer.Client.Addr()),
|
|
answer.ASN, answer.ASName, answer.Country)
|
|
}
|
|
|
|
// setLookupHeaders sets the headers that give the client's AS number and
|
|
// country, leaving out one that is unknown.
|
|
func setLookupHeaders(header http.Header, asn, country string) {
|
|
if asn != "" {
|
|
header.Set(asnHeader, asn)
|
|
}
|
|
|
|
if country != "" {
|
|
header.Set(countryHeader, country)
|
|
}
|
|
}
|
|
|
|
// canBePlaced reports whether a lookup can place addr: private, loopback
|
|
// and link-local addresses have no AS number or country.
|
|
func canBePlaced(addr netip.Addr) bool {
|
|
return !addr.IsPrivate() && !addr.IsLoopback() && !addr.IsLinkLocalUnicast()
|
|
}
|