Skip to content

Instantly share code, notes, and snippets.

@Zoddo
Last active October 29, 2016 20:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zoddo/88043aa6996290f43a5acdbb70ae891c to your computer and use it in GitHub Desktop.
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)
<?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());
--- 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