Skip to content
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
3 changes: 3 additions & 0 deletions .github/actions/apt-x64/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ runs:
ldap-utils \
openssl \
slapd \
bind9 \
bind9utils \
bind9-dnsutils \
language-pack-de \
libgmp-dev \
libicu-dev \
Expand Down
4 changes: 4 additions & 0 deletions .github/actions/setup-x64/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ runs:
run: |
set -x

sudo systemctl stop named
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
6 changes: 6 additions & 0 deletions ext/standard/tests/dns/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
!*.sh
named.conf
named.pid
named.log
managed-keys.*
*.jnl
41 changes: 41 additions & 0 deletions ext/standard/tests/dns/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# DNS tests

These tests run the PHP DNS functions against a local BIND 9 server serving
the zones from `zones/`. They are skipped unless the server is running and
`/etc/resolv.conf` points to it.

Requirements on Debian / Ubuntu:

```sh
sudo apt-get install bind9 bind9utils bind9-dnsutils
```

Nothing else may listen on `127.0.0.1:53` (the system `named` service started
by the package install needs to be stopped). systemd-resolved listens on
`127.0.0.53` and is not a problem.

Running the tests:

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

sapi/cli/php run-tests.php ext/standard/tests/dns

sudo ext/standard/tests/dns/resolv-reset.sh
sudo ext/standard/tests/dns/bind-stop.sh
```

`bind-start.sh` generates `named.conf` from `named.conf.in` and starts `named`
on `127.0.0.1:53` as the owner of this directory. Other queries are forwarded
to the nameservers the host uses so everything else keeps resolving. The
server logs to `named.log`.

`resolv-setup.sh` saves `/etc/resolv.conf` and replaces it with one using
`127.0.0.1` with the previous nameservers as a fallback. The PHP DNS
functions use libresolv which reads this file directly, so this is the only
setting that matters. systemd-resolved is not used on purpose as it re-applies
DHCP servers and routes queries per interface.

To add records, edit `zones/basic.dnstest.php.net.zone` and bump the serial,
or add a new zone file and a `zone` block to `named.conf.in`.
84 changes: 84 additions & 0 deletions ext/standard/tests/dns/bind-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Starts BIND serving the zones from zones/ on 127.0.0.1:53, forwarding
# everything else to the nameservers the host uses. Use -f to run it in
# the foreground.

set -euo pipefail

source "$(dirname "${BASH_SOURCE[0]}")/common.sh"

command -v named >/dev/null || fail "named not found, install BIND 9 (bind9 bind9utils bind9-dnsutils)"

if pid="$(running_pid)"; then
echo "BIND is already running with PID $pid"
exit 0
fi

# Installing bind9 on Debian/Ubuntu starts the system named on port 53
if command -v ss >/dev/null; then
listener="$(ss -H -uln "sport = :$DNS_PORT" | awk -v a="$DNS_ADDRESS:$DNS_PORT" -v p="$DNS_PORT" \
'$4 == a || $4 == "0.0.0.0:" p || $4 == "[::]:" p || $4 == "*:" p { print $4; exit }')"
[[ -z "$listener" ]] || fail "$listener is already in use, stop the service using it first (e.g. systemctl stop named)"
fi

forwarders=""
while read -r ns; do
forwarders+="$ns; "
done < <(upstream_nameservers)
if [[ -n "$forwarders" ]]; then
forwarders="forwarders { $forwarders}; forward first;"
fi

sed -e "s|@TEST_DIR@|$TEST_DIR|g" \
-e "s|@PID_FILE@|$PID_FILE|g" \
-e "s|@LOG_FILE@|$LOG_FILE|g" \
-e "s|@ADDRESS@|$DNS_ADDRESS|g" \
-e "s|@PORT@|$DNS_PORT|g" \
-e "s|@FORWARDERS@|$forwarders|" \
"$TEST_DIR/named.conf.in" > "$NAMED_CONF"
chmod 644 "$NAMED_CONF"

if command -v named-checkconf >/dev/null; then
named-checkconf -z "$NAMED_CONF" >/dev/null
fi

# Drop privileges to the owner of this directory so named can write here
args=(-c "$NAMED_CONF")
owner=""
if [[ "$(id -u)" -eq 0 ]]; then
owner="$(stat -c '%U' "$TEST_DIR")"
if [[ "$owner" != "root" ]]; then
args+=(-u "$owner")
fi
elif [[ "$DNS_PORT" -lt 1024 ]]; then
fail "must run as root to listen on port $DNS_PORT"
fi

# The distribution profile only allows named to read /etc/bind and /var/{cache,lib}/bind
if can_manage_apparmor; then
apparmor_parser -R "$AA_PROFILE" 2>/dev/null || true
fi

rm -f "${PID_FILE:?}" "${ZONES_DIR:?}"/*.jnl
: > "$LOG_FILE"
[[ -z "$owner" ]] || chown "$owner" "$LOG_FILE"

if [[ "${1:-}" == "-f" ]]; then
exec named "${args[@]}" -g
fi

named "${args[@]}"

for _ in $(seq 1 40); do
if pid="$(running_pid)"; then
if ! command -v dig >/dev/null \
|| [[ "$(dig "@$DNS_ADDRESS" -p "$DNS_PORT" +short +time=1 +tries=1 www.basic.dnstest.php.net A)" == "192.0.2.1" ]]; then
echo "BIND started with PID $pid on $DNS_ADDRESS:$DNS_PORT"
exit 0
fi
fi
sleep 0.25
done

cat "$LOG_FILE" >&2
fail "BIND did not start, see $LOG_FILE"
23 changes: 23 additions & 0 deletions ext/standard/tests/dns/bind-stop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Stops BIND started by bind-start.sh

set -euo pipefail

source "$(dirname "${BASH_SOURCE[0]}")/common.sh"

if pid="$(running_pid)"; then
kill "$pid"
for _ in $(seq 1 40); do
kill -0 "$pid" 2>/dev/null || break
sleep 0.25
done
echo "BIND with PID $pid stopped"
else
echo "BIND is not running"
fi

rm -f "${PID_FILE:?}" "${NAMED_CONF:?}" "${ZONES_DIR:?}"/*.jnl

if can_manage_apparmor; then
apparmor_parser -r "$AA_PROFILE" 2>/dev/null || true
fi
22 changes: 22 additions & 0 deletions ext/standard/tests/dns/checkdnsrr_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
checkdnsrr() and dns_check_record() basic usage
--SKIPIF--
<?php require "skipif.inc"; ?>
--FILE--
<?php
var_dump(checkdnsrr('www.basic.dnstest.php.net', 'A'));
var_dump(checkdnsrr('basic.dnstest.php.net', 'MX'));
var_dump(checkdnsrr('basic.dnstest.php.net', 'NS'));
var_dump(checkdnsrr('txt1.basic.dnstest.php.net', 'TXT'));
var_dump(dns_check_record('www.basic.dnstest.php.net', 'A'));
var_dump(checkdnsrr('www.basic.dnstest.php.net', 'MX'));
var_dump(checkdnsrr('nonexistent.basic.dnstest.php.net', 'A'));
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(false)
bool(false)
49 changes: 49 additions & 0 deletions ext/standard/tests/dns/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ZONES_DIR="$TEST_DIR/zones"
NAMED_CONF="$TEST_DIR/named.conf"
PID_FILE="$TEST_DIR/named.pid"
LOG_FILE="$TEST_DIR/named.log"

DNS_ADDRESS="${PHP_DNS_TEST_ADDRESS:-127.0.0.1}"
DNS_PORT="${PHP_DNS_TEST_PORT:-53}"

RESOLV_CONF="/etc/resolv.conf"
RESOLV_CONF_BACKUP="/etc/resolv.conf.php-dns-test.orig"

AA_PROFILE="/etc/apparmor.d/usr.sbin.named"

fail() {
echo "$*" >&2
exit 1
}

resolv_nameservers() {
[[ -r "$1" ]] && sed -nE 's/^[[:space:]]*nameserver[[:space:]]+([^[:space:]#]+).*/\1/p' "$1"
return 0
}

# Nameservers the host uses, skipping loopback (the systemd-resolved stub or our own server)
upstream_nameservers() {
local f ns found=0
for f in "$RESOLV_CONF_BACKUP" /run/systemd/resolve/resolv.conf "$RESOLV_CONF"; do
while read -r ns; do
case "$ns" in
127.*|::1) ;;
*) echo "$ns"; found=1 ;;
esac
done < <(resolv_nameservers "$f")
[[ $found -eq 1 ]] && return 0
done
}

running_pid() {
local pid
pid="$(cat "$PID_FILE" 2>/dev/null)" || return 1
[[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null || return 1
echo "$pid"
}

can_manage_apparmor() {
[[ -f "$AA_PROFILE" && -d /sys/kernel/security/apparmor && "$(id -u)" -eq 0 ]] \
&& command -v apparmor_parser >/dev/null
}
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 with A record
--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(1) {
[0]=>
array(5) {
["host"]=>
string(25) "www.basic.dnstest.php.net"
["class"]=>
string(2) "IN"
["ttl"]=>
int(%d)
["type"]=>
string(1) "A"
["ip"]=>
string(9) "192.0.2.1"
}
}
29 changes: 29 additions & 0 deletions ext/standard/tests/dns/dns_get_record_mx.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
dns_get_record() with MX record
--SKIPIF--
<?php require "skipif.inc"; ?>
--FILE--
<?php
$domain = 'basic.dnstest.php.net';

$result = dns_get_record($domain, DNS_MX);
var_dump($result);
?>
--EXPECTF--
array(1) {
[0]=>
array(6) {
["host"]=>
string(21) "basic.dnstest.php.net"
["class"]=>
string(2) "IN"
["ttl"]=>
int(%d)
["type"]=>
string(2) "MX"
["pri"]=>
int(10)
["target"]=>
string(25) "mx1.basic.dnstest.php.net"
}
}
16 changes: 16 additions & 0 deletions ext/standard/tests/dns/dns_get_record_nonexistent.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
dns_get_record() with a name that does not exist
--SKIPIF--
<?php require "skipif.inc"; ?>
--FILE--
<?php
$domain = 'nonexistent.basic.dnstest.php.net';

var_dump(dns_get_record($domain, DNS_A));
var_dump(dns_get_record($domain, DNS_A, $authns, $addtl, true));
?>
--EXPECT--
array(0) {
}
array(0) {
}
32 changes: 32 additions & 0 deletions ext/standard/tests/dns/dns_get_record_txt.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
dns_get_record() with TXT record
--SKIPIF--
<?php require "skipif.inc"; ?>
--FILE--
<?php
$domain = 'txt1.basic.dnstest.php.net';

$result = dns_get_record($domain, DNS_TXT);
var_dump($result);
?>
--EXPECTF--
array(1) {
[0]=>
array(6) {
["host"]=>
string(26) "txt1.basic.dnstest.php.net"
["class"]=>
string(2) "IN"
["ttl"]=>
int(%d)
["type"]=>
string(3) "TXT"
["txt"]=>
string(25) "This is a test TXT record"
["entries"]=>
array(1) {
[0]=>
string(25) "This is a test TXT record"
}
}
}
27 changes: 27 additions & 0 deletions ext/standard/tests/dns/getmxrr_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
getmxrr() basic usage
--SKIPIF--
<?php require "skipif.inc"; ?>
--FILE--
<?php
var_dump(getmxrr('basic.dnstest.php.net', $hosts, $weights));
var_dump($hosts, $weights);

var_dump(getmxrr('www.basic.dnstest.php.net', $hosts, $weights));
var_dump($hosts, $weights);
?>
--EXPECT--
bool(true)
array(1) {
[0]=>
string(25) "mx1.basic.dnstest.php.net"
}
array(1) {
[0]=>
int(10)
}
bool(false)
array(0) {
}
array(0) {
}
Loading
Loading