Skip to content

Add DNS testing setup using BIND 9 #18693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: PHP-8.3
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ jobs:
ldap-utils \
openssl \
slapd \
bind9 \
bind9utils \
libgmp-dev \
libicu-dev \
libtidy-dev \
Expand Down
3 changes: 3 additions & 0 deletions .github/actions/setup-x64/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ runs:
run: |
set -x

sudo ./ext/standard/tests/dns/bind-start.sh
sudo ./ext/standard/tests/dns/resolv-setup.sh

sudo service slapd start
docker exec sql1 /opt/mssql-tools18/bin/sqlcmd -S 127.0.0.1 -U SA -C -P "<YourStrong@Passw0rd>" -Q "create login pdo_test with password='password', check_policy=off; create user pdo_test for login pdo_test; grant alter, control to pdo_test;"
docker exec sql1 /opt/mssql-tools18/bin/sqlcmd -S 127.0.0.1 -U SA -C -P "<YourStrong@Passw0rd>" -Q "create login odbc_test with password='password', check_policy=off; create user odbc_test for login odbc_test; grant alter, control, delete to odbc_test;"
Expand Down
68 changes: 68 additions & 0 deletions ext/standard/tests/dns/bind-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/bash

set -euo pipefail

# Resolve script location
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ZONES_DIR="$SCRIPT_DIR/zones"
NAMED_CONF="named.conf.local"
PID_FILE="$ZONES_DIR/named.pid"
LOG_FILE="$SCRIPT_DIR/named.log"

# Default mode: background
FOREGROUND=false
if [[ "${1:-}" == "-f" ]]; then
FOREGROUND=true
fi

# Ensure zones directory exists
if [ ! -d "$ZONES_DIR" ]; then
echo "Zone directory $ZONES_DIR not found."
exit 1
fi

# Clean up any leftover journal or PID files
rm -f "$ZONES_DIR"/*.jnl "$PID_FILE"

# Print what we're doing
echo "Starting BIND from $SCRIPT_DIR"

if $FOREGROUND; then
echo "(running in foreground)"
exec named -c "$NAMED_CONF" -p 53 -u "$(whoami)" -t "$SCRIPT_DIR" -g -d 1
else
echo "(running in background)"
named -c "$NAMED_CONF" -p 53 -u "$(whoami)" -t "$SCRIPT_DIR" > "$LOG_FILE" 2>&1

# Wait for BIND to start with periodic checks
MAX_WAIT=20 # Maximum wait time in attempts (20 * 0.5s = 10s)
CHECK_INTERVAL=0.5 # Check every 500ms
ATTEMPTS=0

echo -n "Waiting for BIND to start"

while [[ $ATTEMPTS -lt $MAX_WAIT ]]; do
if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
echo "" # New line after the dots
ELAPSED=$(echo "scale=1; $ATTEMPTS * $CHECK_INTERVAL" | bc 2>/dev/null || echo "${ATTEMPTS}")
echo "BIND started in background with PID $(cat "$PID_FILE") (took ~${ELAPSED}s)"
exit 0
fi

echo -n "."
sleep "$CHECK_INTERVAL"
((ATTEMPTS++))
done

echo "" # New line after the dots
TOTAL_WAIT=$(echo "scale=1; $MAX_WAIT * $CHECK_INTERVAL" | bc 2>/dev/null || echo "${MAX_WAIT}")
echo "Failed to start BIND within ~${TOTAL_WAIT}s. See $LOG_FILE for details."

# Show last few lines of log for debugging
if [[ -f "$LOG_FILE" ]]; then
echo "Last few lines from log:"
tail -5 "$LOG_FILE"
fi

exit 1
fi
20 changes: 20 additions & 0 deletions ext/standard/tests/dns/bind-stop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/bash

set -euo pipefail

# Resolve script location
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ZONES_DIR="$SCRIPT_DIR/zones"
PID_FILE="$ZONES_DIR/named.pid"

if [ -f "$PID_FILE" ]; then
NAMED_PID=$(cat $PID_FILE)
if [ -n "$NAMED_PID" ]; then
echo "Stopping BIND running on pid $NAMED_PID"
kill $NAMED_PID
else
echo "BIND pid is empty"
fi
else
echo "BIND is not running"
fi
27 changes: 27 additions & 0 deletions ext/standard/tests/dns/dns_get_record_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
dns_get_record() basic usage
--SKIPIF--
<?php require "skipif.inc"; ?>
--FILE--
<?php
$domain = 'www.basic.dnstest.php.net';

$result = dns_get_record($domain, DNS_A);
var_dump($result);
?>
--EXPECTF--
array(%d) {
[0]=>
array(%d) {
["host"]=>
string(%d) "www.basic.dnstest.php.net"
["class"]=>
string(2) "IN"
["ttl"]=>
int(%d)
["type"]=>
string(1) "A"
["ip"]=>
string(%d) "192.0.2.1"
}
}
12 changes: 12 additions & 0 deletions ext/standard/tests/dns/named.conf.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
options {
directory "zones";
listen-on port 53 { 127.0.0.1; };
allow-query { any; };
pid-file "named.pid";
recursion yes;
};

zone "basic.dnstest.php.net" {
type master;
file "basic.dnstest.php.net.zone";
};
16 changes: 16 additions & 0 deletions ext/standard/tests/dns/resolv-reset.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/bash
set -euo pipefail

echo "Current DNS configuration:"
resolvectl status | grep -E 'Link|Current DNS Server:|DNS Servers:'

echo -e "\nResetting DNS configuration by restarting systemd-resolved..."
systemctl restart systemd-resolved.service

# Give it a moment to fully restart
sleep 1

echo -e "\nUpdated DNS configuration:"
resolvectl status | grep -E 'Link|Current DNS Server:|DNS Servers:'

echo -e "\nDNS configuration has been reset to original state."
36 changes: 36 additions & 0 deletions ext/standard/tests/dns/resolv-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/bash
set -euo pipefail

LOCAL_DNS="127.0.0.1"

echo "Looking for a DNS-enabled network interface..."

resolvectl status

# Find the interface with DNS and DefaultRoute using grep
IFACE=$(resolvectl status | grep -B1 "Current Scopes: DNS" | grep "Link" | head -n1 | sed -E 's/Link [0-9]+ \(([^)]+)\)/\1/')

if [[ -z "$IFACE" ]]; then
echo "Could not find a suitable interface with DNS configured."
exit 1
fi

echo "Using interface: $IFACE"

# Get current DNS server
echo "Current configuration:"
resolvectl status "$IFACE" | grep -E 'Current DNS Server:|DNS Servers:'

echo "Setting DNS to $LOCAL_DNS for $IFACE"

# Reset interface configuration
resolvectl revert "$IFACE"

# Set DNS to local
resolvectl dns "$IFACE" "$LOCAL_DNS"

# Confirm setup
echo -e "\nUpdated configuration:"
resolvectl status "$IFACE" | grep -E 'Current DNS Server:|DNS Servers:'

echo -e "\nDNS configuration has been updated."
13 changes: 13 additions & 0 deletions ext/standard/tests/dns/skipif.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
// Do not run on Windows
if (substr(PHP_OS, 0, 3) == 'WIN') {
die("skip not for Windows");
}
// Run only if base functions are available
if (!function_exists('dns_get_record')) {
die("skip DNS functions not available");
}
// Run only if BIND server is running
if (!file_exists(__DIR__ . '/zones/named.pid')) {
die("skip BIND server is not running");
}
1 change: 1 addition & 0 deletions ext/standard/tests/dns/zones/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
managed-keys.*
16 changes: 16 additions & 0 deletions ext/standard/tests/dns/zones/basic.dnstest.php.net.zone
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
$TTL 86400
@ IN SOA ns1.basic.dnstest.php.net. hostmaster.basic.dnstest.php.net. (
2025041101 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL

IN NS ns1.basic.dnstest.php.net.

ns1 IN A 127.0.0.1
www IN A 192.0.2.1
mx1 IN A 192.0.2.2
IN MX 10 mx1.basic.dnstest.php.net.

txt1 IN TXT "This is a test TXT record"
Loading