Last active
October 29, 2016 20:22
-
-
Save Zoddo/88043aa6996290f43a5acdbb70ae891c to your computer and use it in GitHub Desktop.
A script to proxy identd requests to hosts NAT'd with PF (Packet Filter) on FreeBSD (for pfSense for example)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
const MAX_PORT = 65535; | |
const REGEX_V4 = '#^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d{1,5})$#'; | |
const REGEX_V6 = '#^([0-9a-f:])\[(\d{1,5})\]$#'; | |
// Listen TCP port 113 | |
$srv = socket_create_listen(113); | |
if (!$srv) { | |
echo "Unable to bind the server on port 113: " . socket_strerror(socket_last_error()) . "\n"; | |
exit(1); | |
} | |
// Since we will fork the process to handle requests, we should handle SIGCHLD | |
// to know when a child exists and call wait() (using pcntl_waitpid()). | |
// This is important, to prevent zombie processes. | |
pcntl_signal(SIGCHLD, 'childSignalHandler'); | |
while($client = socket_accept($srv)) { | |
pcntl_signal_dispatch(); // Call signal handlers | |
if (!$client) { | |
echo "Error while accepting connection: " . socket_strerror(socket_last_error()) . "\n"; | |
continue; | |
} | |
// We have accepted a TCP connection, we now fork the process to handle the request. | |
// So, the main process will be free to receive another connections. | |
$pid = pcntl_fork(); | |
if ($pid == -1) { | |
echo "Error while forking the process\n"; | |
socket_close($client); | |
continue; | |
} elseif ($pid) { | |
// We are in the master process | |
socket_close($client); // We close our socket. The socket in the forked process will remain opened. | |
usleep(300000); // Anti flood, that limit the number of accepted connections to 3 per seconds. | |
pcntl_signal_dispatch(); // Call signal handlers | |
continue; | |
} | |
// We are now in the forked process | |
socket_getpeername($client, $raddr); // We get the IP of the remote peer (of the TCP connection)... | |
$data = socket_read($client, 255, PHP_NORMAL_READ); // ...and we read the request | |
if (!preg_match('#^(\d{1,5})\s*,\s*(\d{1,5})#', $data, $matches)) { | |
echo "Invalid request from $raddr\n"; | |
socket_close($client); | |
exit; // Exit the fork | |
} | |
$lport = (int) $matches[1]; | |
$rport = (int) $matches[2]; | |
// Don't waste our time with shitty requests | |
if ($lport < 1 || $rport < 1 || $lport > MAX_PORT || $rport > MAX_PORT) { | |
echo "Invalid request from $raddr\n"; | |
socket_close($client); | |
exit; // Exit the fork | |
} | |
unset($res); // Safety | |
// Now, we get a (filtered) list of know connections | |
exec(sprintf('pfctl -Pss | grep "%d" | grep "%d" | grep " -> "', $lport, $rport), $res); | |
foreach ($res as $line) { | |
$conn = explode(' ', $line); | |
if (strpos($conn[2], '.')) $regex = REGEX_V4; | |
else $regex = REGEX_V6; | |
if (!preg_match($regex, $conn[2], $lmat) || !preg_match($regex, $conn[5], $rmat) || !preg_match($regex, trim($conn[3], '()'), $omat)) | |
continue; // The line isn't a NAT'd connection | |
// Check if the local port and remote address & port of the line match the request | |
if ($lmat[2] == $lport && $rmat[1] == $raddr && $rmat[2] == $rport) { | |
$laddr = $omat[1]; | |
$oport = $omat[2]; | |
// Makes a connection to the identd deamon of the server originating the connection | |
$sock = socket_create(strpos($laddr, '.') ? AF_INET : AF_INET6, SOCK_STREAM, SOL_TCP); | |
if (!socket_connect($sock, $laddr, 113)) { | |
echo "DEBUG: Unable to open socket to $laddr\n"; | |
socket_close($sock); | |
// It seems there is no identd on that host. But we continue in case we have choose the wrong connection | |
continue; | |
} | |
// The connection is now opened, we send our request | |
socket_write($sock, "$oport,$rport\r\n"); | |
// And read the reply | |
$data = socket_read($sock, 255, PHP_NORMAL_READ); | |
socket_close($sock); | |
if (!preg_match('#^(\d{1,5})\s*,\s*(\d{1,5})\s*((\s*:\s*([A-Za-z0-9_-]+))+)#', $data, $matches)) { | |
echo "Invalid reply from $laddr\n"; | |
continue; | |
} | |
// We have an identd reply. Sending back the reply to the client | |
socket_write($client, "$lport,$rport{$matches[3]}\r\n"); | |
echo "Relayed identd reply to $raddr for $laddr: $lport,$rport{$matches[3]} (original: {$matches[0]})\n"; | |
break; | |
} | |
} | |
socket_close($client); | |
exit; // Exit the fork | |
} | |
function childSignalHandler($signo, $pid=null, $status=null) { | |
// If no pid is provided, that means we're getting the signal from the system. | |
// Let's figure outwhich child process ended | |
if(!$pid) $pid = pcntl_waitpid(-1, $status, WNOHANG); | |
// Make sure we get all of the exited children | |
while($pid > 0) $pid = pcntl_waitpid(-1, $status, WNOHANG); | |
return true; | |
} | |
echo "Exiting... " . socket_strerror(socket_last_error()); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- a/pf_identd_proxy.php 2016-10-29 22:06:15.483957200 +0200 | |
+++ b/pf_identd_proxy.php 2016-10-29 22:13:37.729451416 +0200 | |
@@ -81,6 +81,7 @@ | |
// Makes a connection to the identd deamon of the server originating the connection | |
$sock = socket_create(strpos($laddr, '.') ? AF_INET : AF_INET6, SOCK_STREAM, SOL_TCP); | |
+ if (strpos($laddr, '.')) socket_bind($sock, substr($laddr, 0, strrpos($laddr, '.')) . '.254'); | |
if (!socket_connect($sock, $laddr, 113)) { | |
echo "DEBUG: Unable to open socket to $laddr\n"; | |
socket_close($sock); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment