From 5195837338160134093a54fcc6d81f8a6ae27842 Mon Sep 17 00:00:00 2001 From: Sharvil Shah Date: Thu, 15 Oct 2026 04:17:13 +0530 Subject: [PATCH 1/4] telemetry: add DNS questions to NetworkActivity --- telemetry/v1.proto | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/telemetry/v1.proto b/telemetry/v1.proto index 5c0cf6f..b8d6199 100644 --- a/telemetry/v1.proto +++ b/telemetry/v1.proto @@ -1304,6 +1304,28 @@ message NetworkActivity { // next ID: 19 } + // A DNS question the DNS proxy handled during the window. Set only under + // Process.dns_questions; Process.flows keeps meaning connections. + message DNSQuestion { + optional string id = 1; // a new UUID per question + optional string name = 2; // question name, lowercase + optional uint32 qtype = 3; // 1 = A, 28 = AAAA, 65 = HTTPS + optional string resolver_address = 4; // where the question was sent + optional uint32 resolver_port = 5; + optional int32 protocol = 6; // 17 = UDP, 6 = TCP + optional Flow.Decision decision = 7; + optional Flow.Tier decision_tier = 8; + optional int64 rule_id = 9; + optional string rule_name = 10; + optional uint32 rcode = 11; // unset: no answer reached the client + repeated string answers = 12; // A/AAAA addresses in the answer + optional uint64 bytes_outbound = 13; // question size + optional uint64 bytes_inbound = 14; // answer size, 0 when no answer + optional google.protobuf.Timestamp start_time = 15; // question received + optional google.protobuf.Timestamp close_time = 16; // answer sent, local reply sent, or the proxy gave up + // next ID: 17 + } + // Network activity for a single process message Process { // Information about the process performing network activity @@ -1311,6 +1333,11 @@ message NetworkActivity { // Active flows for this process during the window repeated Flow flows = 2; + + // DNS questions the DNS proxy handled for this process during the window + repeated DNSQuestion dns_questions = 3; + + // next ID: 4 } // All processes with network activity during this window From 0057ef1e09547dce99adf5512c537235154eac3b Mon Sep 17 00:00:00 2001 From: Sharvil Shah Date: Fri, 16 Oct 2026 03:29:57 +0530 Subject: [PATCH 2/4] telemetry: add DNSActivity --- telemetry/v1.proto | 130 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 25 deletions(-) diff --git a/telemetry/v1.proto b/telemetry/v1.proto index b8d6199..e9ae593 100644 --- a/telemetry/v1.proto +++ b/telemetry/v1.proto @@ -1304,28 +1304,6 @@ message NetworkActivity { // next ID: 19 } - // A DNS question the DNS proxy handled during the window. Set only under - // Process.dns_questions; Process.flows keeps meaning connections. - message DNSQuestion { - optional string id = 1; // a new UUID per question - optional string name = 2; // question name, lowercase - optional uint32 qtype = 3; // 1 = A, 28 = AAAA, 65 = HTTPS - optional string resolver_address = 4; // where the question was sent - optional uint32 resolver_port = 5; - optional int32 protocol = 6; // 17 = UDP, 6 = TCP - optional Flow.Decision decision = 7; - optional Flow.Tier decision_tier = 8; - optional int64 rule_id = 9; - optional string rule_name = 10; - optional uint32 rcode = 11; // unset: no answer reached the client - repeated string answers = 12; // A/AAAA addresses in the answer - optional uint64 bytes_outbound = 13; // question size - optional uint64 bytes_inbound = 14; // answer size, 0 when no answer - optional google.protobuf.Timestamp start_time = 15; // question received - optional google.protobuf.Timestamp close_time = 16; // answer sent, local reply sent, or the proxy gave up - // next ID: 17 - } - // Network activity for a single process message Process { // Information about the process performing network activity @@ -1333,14 +1311,115 @@ message NetworkActivity { // Active flows for this process during the window repeated Flow flows = 2; + } + + // All processes with network activity during this window + repeated Process processes = 1; +} + +// DNS questions the DNS proxy handled, per process, in one monitoring window. +// event_time and processed_time are the window start and end, as for +// NetworkActivity. A question is reported once, in the window it closed in, +// so Question.start_time can precede event_time. One Question is one DNS +// question; a retry is another Question. +// +// Not covered: answers the macOS resolver served from its cache, a question +// folded into an identical one already in flight (only the first asker gets +// a row), encrypted DNS (DNS over HTTPS or TLS, Private Relay), and mDNS. A +// cache refresh carries the process whose lookup triggered it. +message DNSActivity { + enum Decision { + DECISION_UNKNOWN = 0; + DECISION_ALLOW = 1; + DECISION_BLOCK = 2; + DECISION_AUDIT = 3; + } + + // The DNS rule field that matched: exact_names or suffixes. The sync + // schema calls these HOSTNAME and DOMAIN. + enum Tier { + TIER_UNKNOWN = 0; + TIER_EXACT_NAME = 1; + TIER_SUFFIX = 2; + } + + // How the question ended. + enum Outcome { + OUTCOME_UNKNOWN = 0; + // The upstream answer arrived and was relayed to the client. + OUTCOME_ANSWERED = 1; + // santanetd replied NXDOMAIN or NODATA; never carries answers. + OUTCOME_LOCAL_REPLY = 2; + // The proxy gave up waiting for upstream. + OUTCOME_TIMEOUT = 3; + // The client's TCP session ended, or stopped reading, first. + OUTCOME_CLIENT_CLOSED = 4; + // A later question on the same TCP session reused the message ID. + OUTCOME_SUPERSEDED = 5; + // An upstream reply the proxy relayed but could not parse. + OUTCOME_MALFORMED_RESPONSE = 6; + // The question did not decode; name, qtype, and decision fields are unset. + OUTCOME_MALFORMED_QUERY = 7; + // Reserved: OUTCOME_OVERRIDE = 8, a local reply carrying the rule's + // addresses in answers (syncv2 DNSResponse KIND_OVERRIDE). + } - // DNS questions the DNS proxy handled for this process during the window - repeated DNSQuestion dns_questions = 3; + // A resource record from the answer section of the reply the client received. + message Answer { + // The record's owner name, lowercase, no trailing dot. + optional string owner = 1; + // RR type: e.g. 1 = A, 5 = CNAME, 28 = AAAA, 65 = HTTPS. + optional uint32 type = 2; + // Seconds, as received. + optional uint32 ttl = 3; + // RDATA in presentation form: the address for A and AAAA, the target name + // for CNAME (same form as owner), RFC 9460 form for HTTPS. Unset for + // other types. + optional string data = 4; + } + message Question { + // A new UUID per question. + optional string id = 1; + // The question name in presentation form: lowercase, no trailing dot, + // "\." for a dot inside a label, IDNs as punycode. The same form as + // NetworkActivity.Flow.remote_hostname. Unset for MALFORMED_QUERY. + optional string name = 2; + // e.g. 1 = A, 28 = AAAA, 65 = HTTPS. + optional uint32 qtype = 3; + // Where the question was addressed, contacted or not. + optional string resolver_address = 4; + optional uint32 resolver_port = 5; + // 17 = UDP, 6 = TCP. + optional int32 protocol = 6; + optional Decision decision = 7; + optional Tier decision_tier = 8; + optional int64 rule_id = 9; + optional string rule_name = 10; + optional Outcome outcome = 11; + // Raw RCODE; set for ANSWERED and LOCAL_REPLY. + optional uint32 rcode = 12; + // At most 32, in section order; none for a local reply today. + repeated Answer answers = 13; + // Question size; TCP's 2-byte length prefix not counted. + optional uint64 bytes_outbound = 14; + // Reply size, 0 when nothing reached the client. + optional uint64 bytes_inbound = 15; + // Question received. + optional google.protobuf.Timestamp start_time = 16; + // The outcome was reached. + optional google.protobuf.Timestamp close_time = 17; + // next ID: 18 + } + + message Process { + optional ProcessInfo process = 1; + repeated Question questions = 2; + // Questions this process asked in the window beyond the 1,000 kept. + optional uint64 questions_dropped = 3; // next ID: 4 } - // All processes with network activity during this window repeated Process processes = 1; } @@ -1409,6 +1488,7 @@ message SantaMessage { XProtect xprotect = 33; NetworkActivity network_activity = 34; ProcSuspendResume proc_suspend_resume = 35; + DNSActivity dns_activity = 36; } } From 2b485412e321ddb8afe3cc3f352c4b2917e81837 Mon Sep 17 00:00:00 2001 From: Sharvil Shah Date: Fri, 16 Oct 2026 05:54:41 +0530 Subject: [PATCH 3/4] Expose DNS telemetry truncation and loss --- telemetry/v1.proto | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/telemetry/v1.proto b/telemetry/v1.proto index e9ae593..462f7dd 100644 --- a/telemetry/v1.proto +++ b/telemetry/v1.proto @@ -1376,6 +1376,8 @@ message DNSActivity { // for CNAME (same form as owner), RFC 9460 form for HTTPS. Unset for // other types. optional string data = 4; + // Data was omitted because it exceeded the collector's byte budget. + optional bool data_truncated = 5; } message Question { @@ -1409,13 +1411,17 @@ message DNSActivity { optional google.protobuf.Timestamp start_time = 16; // The outcome was reached. optional google.protobuf.Timestamp close_time = 17; - // next ID: 18 + // Some answer records were omitted by the count/byte budget or failed parsing. + optional bool answers_truncated = 18; + // next ID: 19 } message Process { optional ProcessInfo process = 1; repeated Question questions = 2; - // Questions this process asked in the window beyond the 1,000 kept. + // Questions omitted by collection capacity limits in this window. + // Reported once per process/window in a summary with no questions. + // A process with no identity reports losses whose attribution budget was full. optional uint64 questions_dropped = 3; // next ID: 4 } From 888836e343cdbf93edf05b10af0bb081117f32eb Mon Sep 17 00:00:00 2001 From: Sharvil Shah Date: Fri, 16 Oct 2026 23:05:47 +0530 Subject: [PATCH 4/4] telemetry: describe rcode and answers without collector limits --- telemetry/v1.proto | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telemetry/v1.proto b/telemetry/v1.proto index 462f7dd..c2e3d97 100644 --- a/telemetry/v1.proto +++ b/telemetry/v1.proto @@ -1399,9 +1399,11 @@ message DNSActivity { optional int64 rule_id = 9; optional string rule_name = 10; optional Outcome outcome = 11; - // Raw RCODE; set for ANSWERED and LOCAL_REPLY. + // The reply's RCODE, with the EDNS extended bits (RFC 6891). Unset when + // no reply carried one. optional uint32 rcode = 12; - // At most 32, in section order; none for a local reply today. + // The reply's answer section, in order. answers_truncated says some + // records were left out. repeated Answer answers = 13; // Question size; TCP's 2-byte length prefix not counted. optional uint64 bytes_outbound = 14;