true, CURLOPT_HEADER => true, CURLOPT_HTTPHEADER => [ 'Sec-Fetch-Site: none', ], CURLOPT_URL => CORE_URL . $address, CURLOPT_COOKIEFILE => COOKIE_FILE, CURLOPT_COOKIEJAR => COOKIE_FILE, CURLOPT_POSTFIELDS => $post, ]); else curl_setopt($req, CURLOPT_URL, $address); if ($tor) curl_setopt($req, CURLOPT_PROXY, TOR_PROXY); else curl_setopt($req, CURLOPT_SSL_VERIFYPEER, false); $result = curl_exec($req); $status_code = curl_getinfo($req, CURLINFO_RESPONSE_CODE); if ($status_code >= 400 OR $result === false) { var_dump($result); var_dump(curl_error($req)); exit($address . ' test failed with status code ' . $status_code . LF); } return $result; } $username = 'check-' . bin2hex(random_bytes(16)); $password = bin2hex(random_bytes(16)); curlTest('/auth/register', [ 'username' => $username, 'password' => $password, ]); curlTest('/auth/logout', []); curlTest('/auth/login', [ 'username' => $username, 'password' => $password, ]); $new_password = bin2hex(random_bytes(16)); curlTest('/auth/password', [ 'current-password' => $password, 'new-password' => $new_password, ]); $password = $new_password; curlTest('/auth/username', [ 'current-password' => $password, 'new-username' => $username . '2', ]); curlTest('/auth/username', [ 'current-password' => $password, 'new-username' => $username, ]); echo 'Created account with username "' . $username . '" and password "' . $password . '".' . LF; function testReg(): string { $subdomain = bin2hex(random_bytes(16)); curlTest('/reg/register', [ 'subdomain' => $subdomain, 'suffix' => SUFFIX, 'action' => 'register', ]); $domain = $subdomain . '.' . SUFFIX; curlTest('/reg/ns', [ 'action' => 'add', 'domain' => $domain, 'ns' => 'ns1.servnest.invalid.', ]); exescape([ CONF['dns']['kdig_path'], '@' . CONF['reg']['address'], $domain, 'NS', ], $output); if (preg_match('/[ \t]+ns1\.servnest\.invalid\.$/Dm', implode(LF, $output)) !== 1) exit('Error: /reg/ns: NS record not set' . LF); curlTest('/reg/ns', [ 'action' => 'delete', 'domain' => $domain, 'ns' => 'ns1.servnest.invalid.', ]); return $domain; } function testNs(string $domain): void { foreach (CONF['ns']['servers'] as $ns) curlTest('/reg/ns', [ 'action' => 'add', 'domain' => $domain, 'ns' => $ns, ]); preg_match('#\(?[0-9a-z-]{16,128}\._domain-verification\.' . preg_quote(CORE_DOMAIN, '#') . '\.)\#', curlTest('/ns/zone-add', []), $matches); curlTest('/reg/ns', [ 'action' => 'add', 'domain' => $domain, 'ns' => $matches['token'], ]); curlTest('/ns/zone-add', [ 'domain' => $domain, ]); curlTest('/reg/ns', [ 'action' => 'delete', 'domain' => $domain, 'ns' => $matches['token'], ]); curlTest('/ns/caa', [ 'action' => 'add', 'subdomain' => '@', 'zone' => $domain, 'ttl-value' => '2', 'ttl-multiplier' => '3600', 'flag' => '0', 'tag' => 'issue', 'value' => 'letsencrypt.org', ]); exescape([ CONF['dns']['kdig_path'], '@' . CONF['reg']['address'], $domain, 'CAA', ], $output); if (preg_match('/^' . preg_quote($domain, '/') . '[ \t]+7200[ \t]+IN[ \t]+CAA[ \t]+0[ \t]+issue[ \t]+"letsencrypt\.org"$/Dm', implode(LF, $output)) !== 1) exit('Error: /ns/caa: CAA record not set' . LF); curlTest('/ns/edit', [ 'zone' => $domain, 'zone-content' => 'aaaa.' . $domain . ' 3600 AAAA ' . CONF['ht']['ipv6_address'] . "\r\n" . '@ 86400 NS ' . CONF['ns']['servers'][0] . "\r\n", ]); exescape([ CONF['dns']['kdig_path'], '@' . CONF['reg']['address'], 'aaaa.' . $domain, 'AAAA', ], $output); if (preg_match('/[ \t]+' . preg_quote(CONF['ht']['ipv6_address'], '/') . '$/Dm', implode(LF, $output)) !== 1) exit('Error: /ns/edit: AAAA record not set' . LF); } function testHt(string $username, string $password): void { define('TEST_CONTENT', 'test-' . bin2hex(random_bytes(16))); file_put_contents(sys_get_temp_dir() . '/index.html', TEST_CONTENT); file_put_contents(sys_get_temp_dir() . '/exec.txt', 'mkdir /_site0- put ' . sys_get_temp_dir() . '/index.html /_site0-/index.html exit '); $process = proc_open(SSHPASS . ' ' . SFTP . ' -o BatchMode=no -b ' . sys_get_temp_dir() . '/exec.txt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P ' . CONF['ht']['public_sftp_port'] . ' ' . $username . '@' . CONF['ht']['sftp_domain'], [0 => ['pipe', 'r']], $pipes); if (is_resource($process) !== true) exit('Can\'t spawn sftp with sshpass.' . LF); fwrite($pipes[0], $password); fclose($pipes[0]); if (proc_close($process) !== 0) exit('File not sent successfully.' . LF); { $ht_subpath = bin2hex(random_bytes(16)); curlTest('/ht/add-subpath', [ 'path' => $ht_subpath, 'dir' => '_site0-', ]); if (curlTest('https://' . CONF['ht']['subpath_domain'] . ':' . HTTPS_PORT . '/' . $ht_subpath . '/') !== TEST_CONTENT) exit('Unexpected subpath response' . LF); curlTest('/ht/del', [ 'site' => 'subpath:' . $ht_subpath, ]); } { $ht_subdomain = 'test3'; curlTest('/ht/add-subdomain', [ 'subdomain' => $ht_subdomain, 'dir' => '_site0-', ]); if (curlTest('https://' . $ht_subdomain . '.' . CONF['ht']['subpath_domain'] . ':' . HTTPS_PORT . '/') !== TEST_CONTENT) exit('Unexpected subpath response' . LF); curlTest('/ht/del', [ 'site' => 'subdomain:' . $ht_subdomain, ]); } { $html = curlTest('/ht/add-onion', [ 'dir' => '_site0-', ]); if (preg_match('#\http\://(?[0-9a-z]{56})\.onion/\#D', $html, $matches) !== 1) exit('Can\'t find onion address.' . LF); sleep(5); // Onion services are not immediately reachable if (curlTest('http://' . $matches['onion'] . '.onion/', tor: true) !== TEST_CONTENT) exit('Unexpected onion service response (' . $matches['onion'] . '.onion)' . LF); curlTest('/ht/del', [ 'site' => 'onion:' . $matches['onion'] . '.onion', ]); } } $domain = testReg(); testNs($domain); testHt($username, $password); curlTest('/auth/unregister', [ 'current-password' => $password, 'delete' => 'on', ]); unlink(COOKIE_FILE); echo 'OK' . LF;