UDP Gateway for OpenVPN-Connections * * This work is distributed within the terms of * creative commons attribution-share alike 2.0 germany * * See http://creativecommons.org/licenses/by-sa/2.0/ for more information * * @author Bernd Holzmueller * @revision 05 * @license http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike 2.0 Germany * @homepage http://oss.tiggerswelt.net/openvpn_proxy.php * @copyright Copyright © 2008 tiggersWelt.net */ define ("OPENVPN_HOST", "192.168.10.254"); define ("OPENVPN_PORT", 5000); class OpenVPN_Client { const FRAME_SIZE = 1544; // Client-Sockets private $tcpFD = null; private $udpFD = null; // Local TCP-Buffer private $tcpBuf = ""; private $tcpLen = -1; // Remote host private $Host = ""; private $Port = 0; // {{{ __construct /** * Create a new OpenVPN Proxy-Client * * @access public * @return void */ function __construct ($socket, $Host, $Port) { if (!is_resource ($socket)) throw new Exception ("Invalid client handle"); $this->tcpFD = $socket; $this->udpFD = socket_create (AF_INET, SOCK_DGRAM, SOL_UDP); $this->Host = $Host; $this->Port = $Port; @socket_set_nonblock ($this->tcpFD); @socket_set_nonblock ($this->udpFD); } // }}} // {{{ handle /** * Handle incoming events * * @access public * @return void */ public function handle () { if (self::finished ()) return; // Run select() on sockets $Sockets = array ($this->tcpFD, $this->udpFD); $E = null; if (socket_select ($Sockets, $E, $E, 0) < 1) return; // Handle incoming data foreach ($Sockets as $Socket) { if ($Socket == $this->udpFD) { // Read data from UDP $From = $this->Host; $Port = $this->Port; $buf = ""; $len = socket_recvfrom ($this->udpFD, $buf, self::FRAME_SIZE, 0, $From, $Port); if (($From != $this->Host) || ($Port != $this->Port)) { # print "UDP->TCP: Skipping packet from wrong source (" . $From . ":" . $Port . ")\n"; continue; } // Prepare TCP-Data $buf = chr (($len >> 8) & 0xFF) . chr ($len & 0xFF) . $buf; // Resend data via TCP socket_send ($this->tcpFD, $buf, $len + 2, 0); # print "UDP->TCP: Wrote " . $len . " bytes\n"; } else { $buf = ""; $len = socket_recv ($this->tcpFD, $buf, self::FRAME_SIZE, 0); // Append received Data to local buffer $this->tcpBuf .= $buf; # print "TCP->UDP: " . $len . " added to buffer\n"; // Try to get packet-length from buffer if (($this->tcpLen < 0) && (strlen ($this->tcpBuf) >= 2)) { $this->tcpLen = (ord ($this->tcpBuf [0]) << 8) + ord ($this->tcpBuf [1]); $this->tcpBuf = substr ($this->tcpBuf, 2); # print "TCP->UDP: Packet-Size " . $this->tcpLen . " bytes, filled " . strlen ($this->tcpBuf) . " bytes\n"; } // Check wheter to forward the packet if (($this->tcpLen >= 0) && (strlen ($this->tcpBuf) >= $this->tcpLen)) { socket_sendto ($this->udpFD, $this->tcpBuf, $this->tcpLen, 0, $this->Host, $this->Port); $this->tcpBuf = ""; $this->tcpLen = -1; # print "TCP->UDP: Buffer flushed\n"; } } if (!$len) self::shutdown (); } } // }}} // {{{ finished /** * Check wheter communication-channel was closed * * @access public * @return bool */ function finished () { return !(is_resource ($this->tcpFD) && is_resource ($this->udpFD)); } // }}} // {{{ shutdown /** * Close all open handles * * @access public * @return void */ public function shutdown () { # print "CLIENT: Shutdown\n"; @socket_shutdown ($this->tcpFD, 2); @socket_shutdown ($this->udpFD, 2); @socket_close ($this->tcpFD, 2); @socket_close ($this->udpFD, 2); $this->tcpFD = $this->udpFD = null; } // }}} } class OpenVPN_Proxy { private $Socket = null; private $Clients = array (); private $RemoteHost = ""; private $RemotePort = 0; // {{{ __construct /** * @access public * @return void */ function __construct ($LocalHost, $LocalPort, $RemoteHost, $RemotePort) { if (!is_resource ($this->Socket = @socket_create (AF_INET, SOCK_STREAM, SOL_TCP))) throw new Exception ("Could not create server-socket"); if (!@socket_bind ($this->Socket, $LocalHost, $LocalPort)) throw new Exception ("Could not bind server-socket"); if (!@socket_listen ($this->Socket, 10)) throw new Exception ("Could not put socket into listening mode"); socket_set_option ($this->Socket, SOL_SOCKET, SO_REUSEADDR, 1); $this->RemoteHost = $RemoteHost; $this->RemotePort = $RemotePort; } // }}} // {{{ handle /** * Accept incoming connections and handle traffic * * @access public * @return void */ public function handle () { // Check for newly incoming connections $Socks = array ($this->Socket); $E = null; if (socket_select ($Socks, $E, $E, 0) > 0) foreach ($Socks as $Socket) if (is_resource ($Client = socket_accept ($Socket))) try { $this->Clients [] = new OpenVPN_Client ($Client, $this->RemoteHost, $this->RemotePort); # print "SERVER: Added new client\n"; } catch (Exception $E) { # print "SERVER: Could not create client-object - " . $E->getMessage () . "\n"; } // Check all clients for incoming data foreach ($this->Clients as $ID=>$Client) { $Client->handle (); if ($Client->finished ()) unset ($this->Clients [$ID]); } } // }}} // {{{ shutdown /** * Stop the proxy * * @access public * @return void */ public function shutdown () { @socket_shutdown ($this->Socket, 2); @socket_close ($this->Socket, 2); foreach ($this->Clients as $Client) $Client->shutdown (); } // }}} } try { $Proxy = new OpenVPN_Proxy ("127.0.0.1", OPENVPN_PORT, OPENVPN_HOST, OPENVPN_PORT); while (true) { $Proxy->handle (); usleep (12); } } catch (Exception $E) { print "Caught Exception: " . $E->getMessage () . "\n"; } ?>