Add a minimal privacy-preserving secure DNS telemetry to check current version (aa12fa45) · Commits · Debian PHP Team / php · GitLab

14 min read Original article ↗
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ Build-Depends: debhelper (>= 9.20160709~),
               locales-all | language-pack-de,
               netbase,
               netcat-openbsd,
               php-common,
               re2c,
               systemtap-sdt-dev,
               tzdata,
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ Build-Depends: debhelper (>= 9.20160709~),
               locales-all | language-pack-de,
               netbase,
               netcat-openbsd,
               php-common,
               re2c,
               systemtap-sdt-dev,
               tzdata,
Original line number Diff line number Diff line
From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@sury.org>
Date: Wed, 11 Feb 2026 13:44:52 +0100
Subject: Add minimal privacy-preserving secure DNS telemetry to check current
 version

---
 main/secure_dns_telemetry.h         | 605 ++++++++++++++++++++++++++++++++++++
 sapi/apache2handler/config.m4       |   2 +
 sapi/apache2handler/php_functions.c |  10 +
 sapi/fpm/config.m4                  |   2 +
 sapi/fpm/fpm/fpm_main.c             |  10 +
 5 files changed, 629 insertions(+)
 create mode 100644 main/secure_dns_telemetry.h

diff --git a/main/secure_dns_telemetry.h b/main/secure_dns_telemetry.h
new file mode 100644
index 0000000..e8fd957
--- /dev/null
+++ b/main/secure_dns_telemetry.h
@@ -0,0 +1,605 @@
+/*
+ * secure_dns_telemetry.h
+ * Client Library for Secure DNS Telemetry (Corrected)
+ * Features:
+ * - Ciphertext Splitting: Splits >63 char payloads into multiple DNS labels
+ * - Strict Memory Safety: No buffer overruns or unaligned access
+ * - Direct UDP Connection: Validates Source IP/Port
+ */
+
+#pragma once
+
+#include <stdint.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sodium.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+/* --- CONFIGURATION --- */
+
+/*
+ * RUNTIME CONFIGURATION:
+ * The telemetry system accepts host and key as runtime parameters.
+ * No build-time configuration is required.
+ *
+ * Usage:
+ *   telemetry_check(host, port, server_pk_b64, package, version)
+ *
+ * Generate server key:
+ *   ./secure_dns_telemetry_gen_key server.key
+ */
+
+#define EDNS_PAYLOAD_SIZE  1232
+#define DNS_LABEL_SIZE	   63
+#define FIXED_PAYLOAD_SIZE 96
+
+/* Logging - override this for integration (e.g., php_error_docref) */
+#ifndef TELEMETRY_LOG
+#define TELEMETRY_LOG(...) fprintf(stderr, "[php-telemetry] " __VA_ARGS__)
+#endif
+
+/* Default DNS port */
+#ifndef TELEMETRY_DNS_PORT
+#define TELEMETRY_DNS_PORT "53"
+#endif
+
+typedef struct {
+	unsigned char pk[crypto_box_PUBLICKEYBYTES];
+	unsigned char sk[crypto_box_SECRETKEYBYTES];
+	unsigned char nonce[crypto_box_NONCEBYTES];
+} session_ctx_t;
+
+/* --- HELPERS --- */
+
+static inline uint16_t
+read_u16(uint8_t **ptrp) {
+	uint16_t val;
+	memcpy(&val, *ptrp, sizeof(val));
+	*ptrp += sizeof(val);
+	return ntohs(val);
+}
+
+static inline uint32_t
+read_u32(uint8_t **ptrp) {
+	uint32_t val;
+	memcpy(&val, *ptrp, sizeof(val));
+	*ptrp += sizeof(val);
+	return ntohl(val);
+}
+
+static inline void
+write_u16(uint8_t **ptrp, uint16_t val) {
+	uint16_t wire = htons(val);
+	memcpy(*ptrp, &wire, sizeof(wire));
+	*ptrp += sizeof(wire);
+}
+
+static inline void
+write_u32(uint8_t **ptrp, uint32_t val) {
+	uint32_t wire = htonl(val);
+	memcpy(*ptrp, &wire, sizeof(wire));
+	*ptrp += sizeof(wire);
+}
+
+static inline int
+validate_package_name(const char *pkg) {
+	if (!pkg || strlen(pkg) == 0 || strlen(pkg) > 63) {
+		return -1;
+	}
+	for (const char *p = pkg; *p; p++) {
+		/* Only allow alphanumeric, dash, dot, underscore */
+		if (!isalnum((unsigned char)*p) && *p != '-' && *p != '.' &&
+		    *p != '_') {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static inline void
+sanitize_version(char *dest, const char *src, size_t dest_size) {
+	const char *start = src;
+	const char *colon = strchr(src, ':');
+	if (colon) {
+		start = colon + 1;
+	}
+
+	size_t i = 0;
+	while (*start != '\0' && *start != '+' && *start != '~' &&
+	       i < dest_size - 1) {
+		dest[i++] = *start++;
+	}
+	dest[i] = '\0';
+}
+
+static inline int
+append_dns_label(uint8_t **ptr, const uint8_t *end, const char *label,
+		 size_t len) {
+	if (len > 63) {
+		return -1;
+	}
+	if (*ptr + len + 1 >= end) {
+		return -1;
+	}
+	*(*ptr)++ = (uint8_t)len;
+	if (len > 0 && label != NULL) {
+		memcpy(*ptr, label, len);
+	}
+	*ptr += len;
+	return 0;
+}
+
+static inline int
+encode_dns_label(uint8_t **ptrp, const uint8_t *end, const uint8_t *src,
+		 size_t src_len) {
+	char b64[DNS_LABEL_SIZE];
+	size_t b64_len = sizeof(b64);
+	size_t max_len = sodium_base64_ENCODED_LEN(
+		src_len, sodium_base64_VARIANT_URLSAFE_NO_PADDING);
+	if (max_len > b64_len) {
+		return -1;
+	}
+	sodium_bin2base64(b64, b64_len, src, src_len,
+			  sodium_base64_VARIANT_URLSAFE_NO_PADDING);
+	return append_dns_label(ptrp, end, b64, strlen(b64));
+}
+
+static inline int
+append_dns_suffix(uint8_t **ptrp, const uint8_t *end, const char *suffix) {
+	char suffix_copy[256];
+	memset(suffix_copy, 0, sizeof(suffix_copy));
+	if (memccpy(suffix_copy, suffix, '\0', sizeof(suffix_copy)) == NULL) {
+		return -1;
+	}
+
+	/* H5 FIX: Use strtok_r for thread safety */
+	char *saveptr;
+	char *token = strtok_r(suffix_copy, ".", &saveptr);
+	while (token) {
+		size_t len = strlen(token);
+		if (append_dns_label(ptrp, end, token, len) != 0) {
+			return -1;
+		}
+		if (len == 0) {
+			/* Root Label was part of the suffix */
+			return 0;
+		}
+		token = strtok_r(NULL, ".", &saveptr);
+	}
+	/* Append Root Label if not part of the suffix */
+	append_dns_label(ptrp, end, NULL, 0);
+
+	return 0;
+}
+
+static inline uint8_t *
+skip_dns_name(uint8_t *ptr, uint8_t *end) {
+	while (ptr < end) {
+		if (*ptr == 0) {
+			/* Root Label */
+			ptr += 1;
+			break;
+		} else if ((*ptr & 0xC0) == 0xC0) {
+			/* C2 FIX: Reject compressed labels to prevent pointer
+			 * attacks */
+			return NULL;
+		}
+
+		/* Regular Label */
+		uint8_t label_len = *ptr;
+		if (ptr + label_len + 1 > end) {
+			return NULL;
+		}
+		ptr += (label_len + 1);
+	}
+	if (ptr > end) {
+		return NULL;
+	}
+	return ptr;
+}
+
+static inline int
+validate_peer(const struct sockaddr *target, const struct sockaddr *source) {
+	if (target->sa_family != source->sa_family) {
+		return 0;
+	}
+	if (target->sa_family == AF_INET) {
+		struct sockaddr_in *t4 = (struct sockaddr_in *)target;
+		struct sockaddr_in *s4 = (struct sockaddr_in *)source;
+		return t4->sin_port == s4->sin_port &&
+		       memcmp(&t4->sin_addr, &s4->sin_addr,
+			      sizeof(t4->sin_addr)) == 0;
+	} else if (target->sa_family == AF_INET6) {
+		struct sockaddr_in6 *t6 = (struct sockaddr_in6 *)target;
+		struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)source;
+		return t6->sin6_port == s6->sin6_port &&
+		       memcmp(&t6->sin6_addr, &s6->sin6_addr,
+			      sizeof(t6->sin6_addr)) == 0;
+	}
+	return 0;
+}
+
+static inline int
+build_edns_packet(unsigned char *buf, size_t buf_len, uint16_t tx_id,
+		  const char *pkg, const char *version, session_ctx_t *ctx,
+		  const unsigned char *server_pk, const char *domain_suffix) {
+	unsigned char *ptr = buf;
+	unsigned char *end = buf + buf_len;
+
+	if (12 > buf_len) {
+		return -1;
+	}
+	write_u16(&ptr, tx_id);	 /* ID */
+	write_u16(&ptr, 0x0000); /* Flags */
+	write_u16(&ptr, 1);	 /* QDCOUNT=1 */
+	write_u16(&ptr, 0);	 /* ANCOUNT=0 */
+	write_u16(&ptr, 0);	 /* NSCOUNT=0 */
+	write_u16(&ptr, 1);	 /* ARCOUNT=1 */
+
+	/* Crypto */
+	crypto_box_keypair(ctx->pk, ctx->sk);
+	randombytes_buf(ctx->nonce, sizeof(ctx->nonce));
+
+	/* M3 FIX: Build versioned payload: v1|pkg|version|timestamp */
+	time_t now = time(NULL);
+	uint8_t padded_payload[FIXED_PAYLOAD_SIZE];
+	memset(padded_payload, 0, FIXED_PAYLOAD_SIZE);
+	int payload_len = snprintf((char *)padded_payload, FIXED_PAYLOAD_SIZE,
+				   "v1|%s|%s|%ld", pkg, version, (long)now);
+	if (payload_len < 0 || payload_len >= FIXED_PAYLOAD_SIZE) {
+		return -1;
+	}
+
+	/* Single encryption */
+	uint8_t ciphertext[FIXED_PAYLOAD_SIZE + crypto_box_MACBYTES];
+	if (crypto_box_easy(ciphertext, padded_payload, FIXED_PAYLOAD_SIZE,
+			    ctx->nonce, server_pk, ctx->sk) != 0) {
+		return -1;
+	}
+
+	/* Base64 encode ciphertext */
+	size_t cipher_len = sizeof(ciphertext);
+	size_t b64_max_len = sodium_base64_ENCODED_LEN(
+		cipher_len, sodium_base64_VARIANT_URLSAFE_NO_PADDING);
+	char b64_cipher[256];
+	if (b64_max_len > sizeof(b64_cipher)) {
+		return -1;
+	}
+	sodium_bin2base64(b64_cipher, sizeof(b64_cipher), ciphertext,
+			  cipher_len, sodium_base64_VARIANT_URLSAFE_NO_PADDING);
+
+	/* Encode public key and nonce labels */
+	if (encode_dns_label(&ptr, end, ctx->pk, crypto_box_PUBLICKEYBYTES) !=
+	    0) {
+		return -1;
+	}
+	if (encode_dns_label(&ptr, end, ctx->nonce, crypto_box_NONCEBYTES) !=
+	    0) {
+		return -1;
+	}
+
+	/* Split base64 ciphertext into DNS labels (max 63 chars each) */
+	size_t b64_len = strlen(b64_cipher);
+	size_t offset = 0;
+	while (offset < b64_len) {
+		size_t chunk_len = b64_len - offset;
+		if (chunk_len > DNS_LABEL_SIZE) {
+			chunk_len = DNS_LABEL_SIZE;
+		}
+		if (append_dns_label(&ptr, end, b64_cipher + offset,
+				     chunk_len) != 0) {
+			return -1;
+		}
+		offset += chunk_len;
+	}
+
+	if (append_dns_suffix(&ptr, end, domain_suffix) != 0) {
+		return -1;
+	}
+
+	if (ptr + 4 > end) {
+		return -1;
+	}
+	write_u16(&ptr, 16); /* TXT QTYPE */
+	write_u16(&ptr, 1);  /* IN QCLASS */
+
+	/* EDNS0 OPT */
+	if (ptr + 11 > end) {
+		return -1;
+	}
+	*ptr++ = 0;			    /* OWNER */
+	write_u16(&ptr, 41);		    /* TYPE */
+	write_u16(&ptr, EDNS_PAYLOAD_SIZE); /* CLASS */
+	write_u32(&ptr, 0);		    /* TTL */
+	write_u16(&ptr, 0);		    /* RDLEN */
+
+	return (int)(ptr - buf);
+}
+
+static inline int
+decrypt_payload(uint8_t *ptr, uint16_t rdlen, session_ctx_t *ctx,
+		const unsigned char *server_pk) {
+	unsigned char *rdata_ptr = ptr;
+	unsigned char *rdata_end = ptr + rdlen;
+
+	fprintf(stderr, "%s: start\n", __func__);
+
+	while (rdata_ptr < rdata_end) {
+		int txt_len = *rdata_ptr;
+		rdata_ptr++;
+		if (rdata_ptr + txt_len > rdata_end) {
+			break;
+		}
+		if (txt_len == 0) {
+			continue;
+		}
+
+		char b64_resp[512];
+		if (txt_len > 511) {
+			txt_len = 511;
+		}
+		memcpy(b64_resp, rdata_ptr, txt_len);
+		b64_resp[txt_len] = '\0';
+
+		size_t bin_len = 0;
+		unsigned char bin[512];
+
+		if (sodium_base642bin(
+			    bin, sizeof(bin), b64_resp, txt_len, NULL, &bin_len,
+			    NULL,
+			    sodium_base64_VARIANT_URLSAFE_NO_PADDING) != 0) {
+			return -1;
+		}
+
+		if (bin_len <= crypto_box_NONCEBYTES + crypto_box_MACBYTES) {
+			return -1;
+		}
+
+		unsigned char *nonce = bin;
+		unsigned char *ciphertext = bin + crypto_box_NONCEBYTES;
+		size_t cipher_len = bin_len - crypto_box_NONCEBYTES;
+
+		/* H3 FIX: Validate plaintext size before decryption */
+		size_t plaintext_len = cipher_len - crypto_box_MACBYTES;
+		if (plaintext_len >= 256) {
+			return -1;
+		}
+
+		unsigned char decrypted[256];
+		if (crypto_box_open_easy(decrypted, ciphertext, cipher_len,
+					 nonce, server_pk, ctx->sk) != 0) {
+			return -1;
+		}
+
+		decrypted[plaintext_len] = '\0';
+		fprintf(stderr, "%s\n", decrypted);
+		if (strstr((char *)decrypted, "\"urgency\":\"high\"") ||
+		    strstr((char *)decrypted, "\"urgency\":\"critical\"") ||
+		    strstr((char *)decrypted, "\"urgency\":\"emergency\"")) {
+			TELEMETRY_LOG("Security Alert: %s\n",
+				      (char *)decrypted);
+		}
+
+		rdata_ptr += txt_len;
+	}
+
+	return 0;
+}
+
+static inline void
+handle_response(unsigned char *buf, int len, session_ctx_t *ctx, uint16_t tx_id,
+		const unsigned char *server_pk) {
+	fprintf(stderr, "%s: start\n", __func__);
+
+	if (len < 12) {
+		return;
+	}
+
+	unsigned char *end = buf + len;
+	unsigned char *ptr = buf;
+	uint16_t resp_id = read_u16(&ptr);
+	if (resp_id != tx_id) {
+		return;
+	}
+
+	/* M5 FIX: Validate DNS response code */
+	uint16_t flags = read_u16(&ptr);
+	uint16_t rcode = flags & 0x000F;
+	if (rcode != 0) {
+		/* RCODE != NOERROR, reject response */
+		return;
+	}
+
+	uint16_t qdcount = read_u16(&ptr);
+	if (qdcount != 1) {
+		return;
+	}
+
+	ptr = skip_dns_name(ptr, end);
+	if (ptr == NULL || ptr + 4 > end) {
+		return;
+	}
+	uint16_t qtype = read_u16(&ptr);
+	if (qtype != 16) {
+		return;
+	}
+	uint16_t qclass = read_u16(&ptr);
+	if (qclass != 1) {
+		return;
+	}
+
+	uint16_t ancount = read_u16(&ptr);
+	for (size_t i = 0; i < ancount; i++) {
+		if (ptr >= end) {
+			return;
+		}
+		ptr = skip_dns_name(ptr, end);
+		if (ptr == NULL || ptr + 10 > end) {
+			return;
+		}
+
+		uint16_t atype = read_u16(&ptr);
+		uint16_t aclass = read_u16(&ptr);
+		uint32_t attl = read_u32(&ptr);
+		uint16_t rdlen = read_u16(&ptr);
+
+		if (ptr + rdlen > end) {
+			return;
+		}
+
+		(void)attl;
+
+		switch (aclass) {
+		case 1:
+			switch (atype) {
+			case 16:
+				decrypt_payload(ptr, rdlen, ctx, server_pk);
+				break;
+			default:
+				break;
+			}
+		default:
+			break;
+		}
+		ptr += rdlen;
+	}
+}
+
+static inline void
+telemetry_check(const char *host, const char *port, const char *server_pk_b64,
+		const char *package_name, const char *raw_version) {
+	if (sodium_init() == -1) {
+		return;
+	}
+
+	/* Decode Base64 public key */
+	unsigned char server_pk[crypto_box_PUBLICKEYBYTES];
+	size_t decoded_len;
+	if (sodium_base642bin(server_pk, sizeof(server_pk), server_pk_b64,
+			      strlen(server_pk_b64), NULL, &decoded_len, NULL,
+			      sodium_base64_VARIANT_URLSAFE_NO_PADDING) != 0 ||
+	    decoded_len != crypto_box_PUBLICKEYBYTES) {
+		/* Invalid key format, disable telemetry */
+		return;
+	}
+
+	/* C1 FIX: Validate package name to prevent injection */
+	if (validate_package_name(package_name) != 0) {
+		return;
+	}
+
+	char clean_ver[64];
+	sanitize_version(clean_ver, raw_version, sizeof(clean_ver));
+
+	session_ctx_t ctx;
+	unsigned char buffer[EDNS_PAYLOAD_SIZE];
+	uint16_t tx_id;
+	randombytes_buf(&tx_id, sizeof(tx_id));
+
+	/* Build domain suffix from host */
+	char domain_suffix[256];
+	snprintf(domain_suffix, sizeof(domain_suffix), "%s.", host);
+
+	int packet_len = build_edns_packet(buffer, sizeof(buffer), tx_id,
+					   package_name, clean_ver, &ctx,
+					   server_pk, domain_suffix);
+	if (packet_len <= 0) {
+		goto cleanup;
+	}
+
+	struct addrinfo hints, *res, *p;
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_DGRAM;
+	if (getaddrinfo(host, port ? port : TELEMETRY_DNS_PORT, &hints, &res) !=
+	    0) {
+		goto cleanup;
+	}
+
+	int sock = -1;
+	struct sockaddr_storage target_addr;
+	socklen_t target_len = 0;
+
+	for (size_t pass = 0; pass < 2; pass++) {
+		for (p = res; p != NULL; p = p->ai_next) {
+			int match = (pass == 0) ? (p->ai_family == AF_INET6)
+						: (p->ai_family == AF_INET);
+			if (match) {
+				sock = socket(p->ai_family, p->ai_socktype,
+					      p->ai_protocol);
+				fprintf(stderr,
+					"Trying %s socket, fd=%d, family=%d\n",
+					(p->ai_family == AF_INET6) ? "IPv6"
+								   : "IPv4",
+					sock, p->ai_family);
+				if (sock >= 0) {
+					memcpy(&target_addr, p->ai_addr,
+					       p->ai_addrlen);
+					target_len = p->ai_addrlen;
+					goto connected;
+				}
+			}
+		}
+	}
+connected:
+	freeaddrinfo(res);
+	if (sock >= 0) {
+		/* M8 FIX: Check setsockopt() return value */
+		struct timeval tv = { 2, 0 };
+		if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv,
+			       sizeof tv) != 0) {
+			close(sock);
+			goto cleanup;
+		}
+
+		/* Connect UDP socket for proper response routing */
+		if (connect(sock, (struct sockaddr *)&target_addr,
+			    target_len) < 0) {
+			fprintf(stderr, "%s: connect failed (%s)\n", __func__,
+				strerror(errno));
+			close(sock);
+			goto cleanup;
+		}
+
+		ssize_t sent = send(sock, (const char *)buffer, packet_len, 0);
+		fprintf(stderr, "%s: send -> %zd (%s)\n", __func__, sent,
+			strerror(errno));
+		if (sent >= 0) {
+			int n = recv(sock, (char *)buffer, sizeof(buffer), 0);
+			fprintf(stderr, "%s: recv -> %d (%s)\n", __func__, n,
+				strerror(errno));
+			if (n > 0) {
+				handle_response(buffer, n, &ctx, tx_id,
+						server_pk);
+			}
+		}
+		close(sock);
+	}
+
+cleanup:
+	sodium_memzero(&ctx, sizeof(ctx));
+	sodium_memzero(buffer, sizeof(buffer));
+	sodium_memzero(clean_ver, sizeof(clean_ver));
+	sodium_memzero(server_pk, sizeof(server_pk));
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sapi/apache2handler/config.m4 b/sapi/apache2handler/config.m4
index c295f04..12e4efd 100644
--- a/sapi/apache2handler/config.m4
+++ b/sapi/apache2handler/config.m4
@@ -71,6 +71,8 @@ if test "$PHP_APXS2" != "no"; then
   LIBPHP_CFLAGS="-shared"
   PHP_SUBST(LIBPHP_CFLAGS)
 
+  PHP_ADD_LIBRARY(sodium)
+
   case $host_alias in
   *aix*)
     EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-brtl -Wl,-bI:$APXS_LIBEXECDIR/httpd.exp"
diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c
index 27b8aab..7bf905f 100644
--- a/sapi/apache2handler/php_functions.c
+++ b/sapi/apache2handler/php_functions.c
@@ -480,12 +480,22 @@ PHP_INI_BEGIN()
 	STD_PHP_INI_BOOLEAN("last_modified",	"0",	PHP_INI_ALL,	OnUpdateBool,	last_modified,	php_apache2_info_struct, php_apache2_info)
 PHP_INI_END()
 
+#define TELEMETRY_LOG(...)
+#include "secure_dns_telemetry.h"
+
 static PHP_MINIT_FUNCTION(apache)
 {
 #ifdef ZTS
 	ts_allocate_id(&php_apache2_info_id, sizeof(php_apache2_info_struct), (ts_allocate_ctor) NULL, NULL);
 #endif
 	REGISTER_INI_ENTRIES();
+#ifdef TELEMETRY_HOST
+	telemetry_check(TELEMETRY_HOST,
+					TELEMETRY_PORT,
+					TELEMETRY_PK,
+					TELEMETRY_PACKAGE "-fpm",
+					TELEMETRY_VERSION);
+#endif
 	return SUCCESS;
 }
 
diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4
index b9b4dda..81184df 100644
--- a/sapi/fpm/config.m4
+++ b/sapi/fpm/config.m4
@@ -604,6 +604,8 @@ if test "$PHP_FPM" != "no"; then
     php_fpm_systemd=simple
   fi
 
+  PHP_ADD_LIBRARY(sodium)
+
   if test "$PHP_FPM_ACL" != "no" ; then
     AC_MSG_CHECKING([for acl user/group permissions support])
     AC_CHECK_HEADERS([sys/acl.h])
diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c
index a4b1162..1332930 100644
--- a/sapi/fpm/fpm/fpm_main.c
+++ b/sapi/fpm/fpm/fpm_main.c
@@ -1449,6 +1449,9 @@ static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals)
 }
 /* }}} */
 
+#define TELEMETRY_LOG(...)
+#include "secure_dns_telemetry.h"
+
 /* {{{ PHP_MINIT_FUNCTION */
 static PHP_MINIT_FUNCTION(cgi)
 {
@@ -1458,6 +1461,13 @@ static PHP_MINIT_FUNCTION(cgi)
 	php_cgi_globals_ctor(&php_cgi_globals);
 #endif
 	REGISTER_INI_ENTRIES();
+#ifdef TELEMETRY_HOST
+	telemetry_check(TELEMETRY_HOST,
+					TELEMETRY_PORT,
+					TELEMETRY_PK,
+					TELEMETRY_PACKAGE "-fpm",
+					TELEMETRY_VERSION);
+#endif
 	return SUCCESS;
 }
 /* }}} */
Original line number Diff line number Diff line
@@ -45,3 +45,4 @@
0045-Remove-timestamps-from-phar.patch
0046-Disable-garbage-collection-routine.patch
0047-fix-implicit-function-declaration.patch
0048-Add-minimal-privacy-preserving-secure-DNS-telemetry-.patch
Original line number Diff line number Diff line
@@ -143,6 +143,12 @@ endif
DEB_CFLAGS_MAINT_APPEND += -Wall -pedantic -fsigned-char -fno-strict-aliasing 
DEB_CFLAGS_MAINT_APPEND += $(shell getconf LFS_CFLAGS)

# Secure DNS Telemetry (not enabled in build by default)
-include /usr/lib/php/php-common.mk
DEB_CFLAGS_MAINT_APPEND += \
	-DTELEMETRY_PACKAGE='\"$(DEB_SOURCE)\"' \
	-DTELEMETRY_VERSION='\"$(DEB_VERSION)\"'

# OpenSSL 3.0 support
DEB_CFLAGS_MAINT_APPEND += -DOPENSSL_SUPPRESS_DEPRECATED