diff --git a/README.md b/README.md index c5b791c..38e3ede 100644 --- a/README.md +++ b/README.md @@ -64,19 +64,53 @@ make docker CHECKER_VERSION=1.2.3 Set the `endpoint` admin option for the SRV checker to the URL of the running checker-srv server (e.g., `http://checker-srv:8080`). happyDomain will delegate observation collection to this endpoint. -## Rules +## Protocol -| Code | Description | Severity | -|-------------------------------|---------------------------------------------------------------------------------------------------|---------------------| -| `srv_records_present` | At least one SRV record is published for this service. | CRITICAL | -| `srv_null_target` | Detects SRV records with target `.`, signaling the service is intentionally not available. | WARNING | -| `srv_target_not_cname` | RFC 2782: SRV targets must resolve directly to A/AAAA, not through a CNAME. | WARNING | -| `srv_targets_resolve` | Every SRV target resolves to at least one A/AAAA address. | CRITICAL | -| `srv_port_valid` | SRV records advertise a non-zero port that clients can actually connect to. | CRITICAL | -| `srv_priority_weight_sanity` | Priority/weight values follow RFC 2782 conventions (failover present, weights meaningful). | WARNING | -| `srv_tcp_reachable` | Every TCP SRV `target:port` accepts a TCP connection. | CRITICAL | -| `srv_udp_reachable` | UDP SRV targets do not return ICMP port-unreachable. | WARNING | -| `srv_redundancy` | At least two distinct SRV targets exist (avoids single point of failure). | INFO | +### POST /collect + +Request: +```json +{ + "key": "srv_records", + "target": {"userId": "...", "domainId": "...", "serviceId": "..."}, + "options": { + "service": {"_svctype": "svcs.UnknownSRV", "Service": {"srv": [ /* dns.SRV records */ ]}}, + "subdomain": "_sip._tcp", + "domain": "example.com", + "tcpTimeout": 3000, + "udpTimeout": 2000 + } +} +``` + +Response: +```json +{ + "data": { + "serviceDomain": "_sip._tcp.example.com", + "records": [ + { + "service": "sip", + "proto": "tcp", + "owner": "_sip._tcp.example.com", + "target": "sip1.example.com", + "port": 5061, + "priority": 10, + "weight": 20, + "addresses": ["203.0.113.5"], + "probes": [ + { + "address": "203.0.113.5:5061", + "proto": "tcp", + "connected": true, + "latencyMs": 12.4 + } + ] + } + ] + } +} +``` ## Discovered TLS endpoints @@ -98,6 +132,18 @@ so that the TLS checker can probe the targets without re-parsing SRV data: Null targets (`.`) and unknown service names are skipped. SNI is set to the SRV target hostname. +## Rules + +| Name | Description | +|---|---| +| `srv_records_present` | At least one SRV record is published. | +| `srv_null_target` | Detects `"."` targets (RFC 2782, service intentionally unavailable). | +| `srv_target_not_cname` | Warns when an SRV target is a CNAME (RFC 2782 violation). | +| `srv_targets_resolve` | Every target resolves to at least one A/AAAA. | +| `srv_tcp_reachable` | Every `_tcp` SRV `target:port` accepts a TCP connection. | +| `srv_udp_reachable` | UDP targets do not return ICMP port-unreachable. | +| `srv_redundancy` | At least two distinct targets exist (no single point of failure). | + ## License & licensing roadmap This project is currently licensed under the **GNU Affero General Public diff --git a/checker/collect.go b/checker/collect.go index 83f37df..4a410fb 100644 --- a/checker/collect.go +++ b/checker/collect.go @@ -86,10 +86,7 @@ func (p *srvProvider) Collect(ctx context.Context, opts sdk.CheckerOptions) (any } for _, r := range payload.Records { - // Hdr.Name is relative to the service location (serviceDomain = - // subdomain.domain), so we join it with serviceDomain before - // treating as FQDN. - owner := sdk.JoinRelative(r.Hdr.Name, serviceDomain) + owner := strings.TrimSuffix(r.Hdr.Name, ".") svc, proto := parseOwner(owner, serviceDomain) rec := SRVRecord{