mirror of
https://github.com/cunnie/sslip.io.git
synced 2025-10-07 00:23:44 +08:00
Metrics: track the use of TCP vs. UDP
Why implement a feature w/out measuring how much it gets used? I want to know who, if anyone, is using TCP queries. TODO: update the documentation.
This commit is contained in:
@@ -20,22 +20,35 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
var actualMetrics xip.Metrics
|
var actualMetrics xip.Metrics
|
||||||
expectedMetrics := getMetrics(port)
|
expectedMetrics := getMetrics(port)
|
||||||
|
|
||||||
// A updates .Queries, .AnsweredQueries, .AnsweredAQueries
|
// A updates .Queries, UDPQueries .AnsweredQueries, .AnsweredAQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics.AnsweredAQueries++
|
expectedMetrics.AnsweredAQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost 127.0.0.1.sslip.io +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost 127.0.0.1.sslip.io +short -p "+strconv.Itoa(port), port)
|
||||||
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
||||||
|
|
||||||
|
// A updates .Queries, TCPQueries .AnsweredQueries, .AnsweredAQueries
|
||||||
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.TCPQueries++
|
||||||
|
expectedMetrics.AnsweredQueries++
|
||||||
|
expectedMetrics.AnsweredAQueries++
|
||||||
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
|
// "+vc" runs the lookup over TCP, not UDP
|
||||||
|
actualMetrics = digAndGetMetrics("@localhost 127.0.0.1.sslip.io +short +vc -p "+strconv.Itoa(port), port)
|
||||||
|
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
||||||
|
|
||||||
// A (non-existent) record updates .Queries
|
// A (non-existent) record updates .Queries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io +short -p "+strconv.Itoa(port), port)
|
||||||
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
||||||
|
|
||||||
// A blocked updates .Queries, .AnsweredQueries, .AnsweredBlockedQueries
|
// A blocked updates .Queries, .AnsweredQueries, .AnsweredBlockedQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics.AnsweredBlockedQueries++
|
expectedMetrics.AnsweredBlockedQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
@@ -45,6 +58,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// AAAA updates .Queries, .AnsweredQueries, .AnsweredAAAAQueries
|
// AAAA updates .Queries, .AnsweredQueries, .AnsweredAAAAQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics.AnsweredAAAAQueries++
|
expectedMetrics.AnsweredAAAAQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
@@ -53,12 +67,14 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// AAAA (non-existent) updates .Queries
|
// AAAA (non-existent) updates .Queries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io aaaa +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io aaaa +short -p "+strconv.Itoa(port), port)
|
||||||
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
||||||
|
|
||||||
// MX (customized) updates .Queries, .AnsweredQueries
|
// MX (customized) updates .Queries, .AnsweredQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost sslip.io mx +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost sslip.io mx +short -p "+strconv.Itoa(port), port)
|
||||||
@@ -66,6 +82,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// MX updates .Queries, AnsweredQueries
|
// MX updates .Queries, AnsweredQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io mx +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io mx +short -p "+strconv.Itoa(port), port)
|
||||||
@@ -73,6 +90,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// NS updates .Queries, AnsweredQueries
|
// NS updates .Queries, AnsweredQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io ns +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io ns +short -p "+strconv.Itoa(port), port)
|
||||||
@@ -80,6 +98,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// NS DNS-01 challenge record updates .Queries, .AnsweredNSDNS01ChallengeQueries
|
// NS DNS-01 challenge record updates .Queries, .AnsweredNSDNS01ChallengeQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
// DNS-01 challenges don't count as successful because we're not authoritative; we're delegating
|
// DNS-01 challenges don't count as successful because we're not authoritative; we're delegating
|
||||||
expectedMetrics.AnsweredNSDNS01ChallengeQueries++
|
expectedMetrics.AnsweredNSDNS01ChallengeQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
@@ -88,6 +107,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// Always successful: SOA
|
// Always successful: SOA
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
dig("@localhost non-existent.sslip.io soa +short -p " + strconv.Itoa(port))
|
dig("@localhost non-existent.sslip.io soa +short -p " + strconv.Itoa(port))
|
||||||
@@ -96,6 +116,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// TXT sslip.io (customized) updates .Queries, .AnsweredQueries,
|
// TXT sslip.io (customized) updates .Queries, .AnsweredQueries,
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost sslip.io txt +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost sslip.io txt +short -p "+strconv.Itoa(port), port)
|
||||||
@@ -103,12 +124,14 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// TXT sslip.io (non-existent) updates .Queries, .AnsweredQueries,
|
// TXT sslip.io (non-existent) updates .Queries, .AnsweredQueries,
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io txt +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io txt +short -p "+strconv.Itoa(port), port)
|
||||||
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
|
||||||
|
|
||||||
// TXT ip.sslip.io updates .Queries, .AnsweredQueries, .AnsweredTXTSrcIPQueries
|
// TXT ip.sslip.io updates .Queries, .AnsweredQueries, .AnsweredTXTSrcIPQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics.AnsweredTXTSrcIPQueries++
|
expectedMetrics.AnsweredTXTSrcIPQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
@@ -117,6 +140,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// TXT version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredTXTVersionQueries
|
// TXT version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredTXTVersionQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics.AnsweredTXTVersionQueries++
|
expectedMetrics.AnsweredTXTVersionQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
@@ -125,6 +149,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// PTR version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredPTRQueriesIPv4
|
// PTR version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredPTRQueriesIPv4
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics.AnsweredPTRQueriesIPv4++
|
expectedMetrics.AnsweredPTRQueriesIPv4++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
@@ -133,6 +158,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// PTR version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredPTRQueriesIPv6
|
// PTR version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredPTRQueriesIPv6
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredQueries++
|
expectedMetrics.AnsweredQueries++
|
||||||
expectedMetrics.AnsweredPTRQueriesIPv6++
|
expectedMetrics.AnsweredPTRQueriesIPv6++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
@@ -141,6 +167,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
|
|
||||||
// TXT DNS-01 challenge record updates .Queries, .AnsweredNSDNS01ChallengeQueries
|
// TXT DNS-01 challenge record updates .Queries, .AnsweredNSDNS01ChallengeQueries
|
||||||
expectedMetrics.Queries++
|
expectedMetrics.Queries++
|
||||||
|
expectedMetrics.UDPQueries++
|
||||||
expectedMetrics.AnsweredNSDNS01ChallengeQueries++
|
expectedMetrics.AnsweredNSDNS01ChallengeQueries++
|
||||||
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
|
||||||
actualMetrics = digAndGetMetrics("@localhost _acme-challenge.fe80--.sslip.io txt +short -p "+strconv.Itoa(port), port)
|
actualMetrics = digAndGetMetrics("@localhost _acme-challenge.fe80--.sslip.io txt +short -p "+strconv.Itoa(port), port)
|
||||||
@@ -154,6 +181,7 @@ var _ = Describe("IntegrationMetrics", func() {
|
|||||||
// the Heisenberg uncertainty principle (observing changes the values)
|
// the Heisenberg uncertainty principle (observing changes the values)
|
||||||
func bumpExpectedToAccountForMetricsQuery(metrics xip.Metrics) xip.Metrics {
|
func bumpExpectedToAccountForMetricsQuery(metrics xip.Metrics) xip.Metrics {
|
||||||
metrics.Queries++
|
metrics.Queries++
|
||||||
|
metrics.UDPQueries++
|
||||||
metrics.AnsweredQueries++
|
metrics.AnsweredQueries++
|
||||||
return metrics
|
return metrics
|
||||||
}
|
}
|
||||||
@@ -181,6 +209,7 @@ func getMetrics(port int) (m xip.Metrics) {
|
|||||||
"\"Uptime: %d\"\n"+
|
"\"Uptime: %d\"\n"+
|
||||||
"\"Blocklist: %s %s %s\n"+
|
"\"Blocklist: %s %s %s\n"+
|
||||||
"\"Queries: %d (%s\n"+ // %s "swallows" the `/s"` at the end
|
"\"Queries: %d (%s\n"+ // %s "swallows" the `/s"` at the end
|
||||||
|
"\"TCP/UDP: %d/%d\"\n"+
|
||||||
"\"Answered Queries: %d (%s\n"+ // %s "swallows" the `/s"` at the end
|
"\"Answered Queries: %d (%s\n"+ // %s "swallows" the `/s"` at the end
|
||||||
"\"A: %d\"\n"+
|
"\"A: %d\"\n"+
|
||||||
"\"AAAA: %d\"\n"+
|
"\"AAAA: %d\"\n"+
|
||||||
@@ -192,6 +221,7 @@ func getMetrics(port int) (m xip.Metrics) {
|
|||||||
&uptime,
|
&uptime,
|
||||||
&junk, &junk, &junk,
|
&junk, &junk, &junk,
|
||||||
&m.Queries, &junk,
|
&m.Queries, &junk,
|
||||||
|
&m.TCPQueries, &m.UDPQueries,
|
||||||
&m.AnsweredQueries, &junk,
|
&m.AnsweredQueries, &junk,
|
||||||
&m.AnsweredAQueries,
|
&m.AnsweredAQueries,
|
||||||
&m.AnsweredAAAAQueries,
|
&m.AnsweredAAAAQueries,
|
||||||
|
@@ -130,6 +130,7 @@ func readFromUDP(conn *net.UDPConn, x *xip.Xip, quiet bool) {
|
|||||||
if !quiet {
|
if !quiet {
|
||||||
log.Printf("%v.%d %s", addr.IP, addr.Port, logMessage)
|
log.Printf("%v.%d %s", addr.IP, addr.Port, logMessage)
|
||||||
}
|
}
|
||||||
|
x.Metrics.UDPQueries += 1
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +168,7 @@ func readFromTCP(tcpListener *net.TCPListener, x *xip.Xip, quiet bool) {
|
|||||||
if !quiet {
|
if !quiet {
|
||||||
log.Printf("%s.%s %s", addr, port, logMessage)
|
log.Printf("%s.%s %s", addr, port, logMessage)
|
||||||
}
|
}
|
||||||
|
x.Metrics.TCPQueries += 1
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,8 @@ type Xip struct {
|
|||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
Start time.Time
|
Start time.Time
|
||||||
Queries int
|
Queries int
|
||||||
|
TCPQueries int
|
||||||
|
UDPQueries int
|
||||||
AnsweredQueries int
|
AnsweredQueries int
|
||||||
AnsweredAQueries int
|
AnsweredAQueries int
|
||||||
AnsweredAAAAQueries int
|
AnsweredAAAAQueries int
|
||||||
@@ -892,6 +894,7 @@ func TXTMetrics(x *Xip, _ net.IP) (txtResources []dnsmessage.TXTResource, err er
|
|||||||
len(x.BlocklistStrings),
|
len(x.BlocklistStrings),
|
||||||
len(x.BlocklistCDIRs)))
|
len(x.BlocklistCDIRs)))
|
||||||
metrics = append(metrics, fmt.Sprintf("Queries: %d (%.1f/s)", x.Metrics.Queries, float64(x.Metrics.Queries)/uptime.Seconds()))
|
metrics = append(metrics, fmt.Sprintf("Queries: %d (%.1f/s)", x.Metrics.Queries, float64(x.Metrics.Queries)/uptime.Seconds()))
|
||||||
|
metrics = append(metrics, fmt.Sprintf("TCP/UDP: %d/%d", x.Metrics.TCPQueries, x.Metrics.UDPQueries))
|
||||||
metrics = append(metrics, fmt.Sprintf("Answered Queries: %d (%.1f/s)", x.Metrics.AnsweredQueries, float64(x.Metrics.AnsweredQueries)/uptime.Seconds()))
|
metrics = append(metrics, fmt.Sprintf("Answered Queries: %d (%.1f/s)", x.Metrics.AnsweredQueries, float64(x.Metrics.AnsweredQueries)/uptime.Seconds()))
|
||||||
metrics = append(metrics, fmt.Sprintf("A: %d", x.Metrics.AnsweredAQueries))
|
metrics = append(metrics, fmt.Sprintf("A: %d", x.Metrics.AnsweredAQueries))
|
||||||
metrics = append(metrics, fmt.Sprintf("AAAA: %d", x.Metrics.AnsweredAAAAQueries))
|
metrics = append(metrics, fmt.Sprintf("AAAA: %d", x.Metrics.AnsweredAAAAQueries))
|
||||||
@@ -920,6 +923,8 @@ func soaLogMessage(soaResource dnsmessage.SOAResource) string {
|
|||||||
// MostlyEquals compares all fields except `Start` (timestamp)
|
// MostlyEquals compares all fields except `Start` (timestamp)
|
||||||
func (a Metrics) MostlyEquals(b Metrics) bool {
|
func (a Metrics) MostlyEquals(b Metrics) bool {
|
||||||
if a.Queries == b.Queries &&
|
if a.Queries == b.Queries &&
|
||||||
|
a.TCPQueries == b.TCPQueries &&
|
||||||
|
a.UDPQueries == b.UDPQueries &&
|
||||||
a.AnsweredQueries == b.AnsweredQueries &&
|
a.AnsweredQueries == b.AnsweredQueries &&
|
||||||
a.AnsweredAQueries == b.AnsweredAQueries &&
|
a.AnsweredAQueries == b.AnsweredAQueries &&
|
||||||
a.AnsweredAAAAQueries == b.AnsweredAAAAQueries &&
|
a.AnsweredAAAAQueries == b.AnsweredAAAAQueries &&
|
||||||
|
Reference in New Issue
Block a user