PHP Sockets - can only connect from localhost (Port Forwarding problem?)

13,992

Solution 1

You're binding the socket (using socket_bind) onto localhost. Supplying localhost there will have PHP bind the socket to the 127.0.0.1.

socket_bind is used to bind a socket to a specific interface. Per example:

socket_bind($socket, '127.0.0.1', 80);

This allows you to connect to 127.0.0.1:80, but not 192.168.1.100:80, even if they are the same machine. The socket is bound to the 127.0.0.1 interface only:

$ telnet localhost 80
Trying 127.0.0.1...
Connected to 127.0.0.1.
$ telnet 192.168.1.100 80
Trying 192.168.1.100...
telnet: Unable to connect to remote host: Connection refused

If you want to bind the socket on all available interfaces, use:

socket_bind($socket, '0.0.0.0', 80);

Then this works:

$ telnet localhost 80
Trying 127.0.0.1...
Connected to 127.0.0.1.
$ telnet 192.168.1.100 80
Trying 192.168.1.100...
Connected to 192.168.1.100.

Solution 2

I use this "port_forwarding.php" on server to open port 3000 and forward remote mysql client connection to unix socket file / port 3306 of mysql server:

<?
set_time_limit(0);
function shutdown()
         {global $ipsock, $rmsock;
          if ($ipsock) fclose($ipsock);
          if ($rmsock) fclose($rmsock);
         }
register_shutdown_function('shutdown');

$target_socket='unix:///tmp/mysql.sock';//or 'tcp://192.168.0.2:3306'
$ipsock=stream_socket_server('tcp://192.168.0.2:3000', $errno2, $errstr2);
stream_set_blocking($ipsock, 0);

while (true)
      {usleep(5000);//0.005s, to reduce cpu consumption
       $c_ipsock=stream_socket_accept($ipsock); //even add '-1', it won't wait
       $rmsock=stream_socket_client($target_socket, $errno, $errstr);
       @stream_set_blocking($rmsock, 1);
       while (($c_ipsock && !feof($c_ipsock)) && ($rmsock && !feof($rmsock)))
             {$swrite=$except=null;
              $sread=array($c_ipsock, $rmsock);
              stream_select($sread, $swrite, $except, 5);
              //print_r($sread);echo "    \n";
              if ($sread[0]===$rmsock)
                 {if ($data=fread($rmsock, 65536))
                     {//echo 'rmsock:'.strlen($data).'    '.$data."    \n";
                      myfwrite($c_ipsock, $data);
                     }
                 }
              else if ($sread[0]===$c_ipsock)
                   {if ($data=fread($c_ipsock, 65536))
                       {//echo 'ipsock:'.strlen($data).'    '.$data."    \n";
                        myfwrite($rmsock, $data);
                       }
                   }
              //var_export(array(feof($c_ipsock), feof($rmsock)));echo "   \n";
             }
       @fclose($c_ipsock);
       @fclose($rmsock);
      }

    function myfwrite($fd,$buf) {
        $i=0;
        while ($buf != "") {
            $i=fwrite ($fd,$buf,strlen($buf));
            if ($i==false) {
                if (!feof($fd)) continue;
                break;
            }
            $buf=substr($buf,$i);
        }
        return $i;
    }
?>
Share:
13,992
C-speed
Author by

C-speed

Updated on June 25, 2022

Comments

  • C-speed
    C-speed almost 2 years

    First of all, thanks for taking the time to read this. I have a strange problem with PHP sockets. I working on a php socket daemon which works via localhost, but when I try to connect from outside the LAN or another PC, it doesn't work. I've simplified my daemon to a very basic socket connection to replicate the issue for you to see.

    Basically, here's the senario. I start the socket daemon on my server on port 6667. I can connect to the daemon via telnet and from the browser on the local machine running the daemon, but I cannot from any other machine - the daemon doesn't even see a connection attempt being made.

    To further complicate the issue (which is why I think it's a port forwarding issue), my ISP blocks port 80, so I've setup dyndns and my router to use port 8000. I've also setup my router to forward port 6667 to my server.

    To access my daemon from a browser, I enter the following (seudo) url:

    http://mydomain.com:8000/client.php
    

    This works from the local machine and will connect, but from any other machine, the daemon doesn't even see a connection attempt being made. However, if I specify the port like this:

    http://mydomain.com:6667
    

    my daemon does see a connection being made, but of course then the browser doesn't have a client page loaded that the user can use to interact with the daemon.

    My client uses a flash file to create the socket connection (jsocket), but I know it isn't the cross-domain policy file because the policy is correct, and when connecting via localhost, it serves the policy file correctly.

    Here's the simplified daemon code:

     <? 
    
    // set some variables 
    $host = '0.0.0.0'; 
    $port = 6667; 
    
    // don't timeout! 
    set_time_limit(0); 
    
    // create socket 
    $socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n"); 
    
    // bind socket to port 
    $result = socket_bind($socket, $host, $port) or die("Could not bind to socket\n"); 
    
    // start listening for connections 
    $result = socket_listen($socket, 3) or die("Could not set up socket listener\n"); 
    
    // accept incoming connections 
    // spawn another socket to handle communication 
    $spawn = socket_accept($socket) or die("Could not accept incoming connection\n"); 
    
    // read client input 
    $input = socket_read($spawn, 1024) or die("Could not read input\n"); 
    
    // clean up input string 
    $input = trim($input); 
    
    // echo input back 
    $output = $input . "\n"; 
    socket_write($spawn, $output, strlen ($output)) or die("Could not write output\n"); 
    
    // close sockets 
    socket_close($spawn); 
    socket_close($socket); 
    ?>
    

    Summary:

    I CAN connect from localhost via telnet and browser... I CAN connect from other machines via telnet, but I CAN NOT connection from the browser from other machines using the ip or domain name when port 8000 is specified. The daemon doesn't see any connection attempt. If I specify port 6667, then the daemon see's a connection attempt, but that is useless to the user. :(

    Any help in this matter would be greatly appreciated! Thanks!

  • C-speed
    C-speed about 13 years
    netcoder: Thanks for your input. I accidentally forgot to edit the code I posted above. I changed it to "localhost" do some testing. I tried it with 0.0.0.0 and I get the same results. Local machine can connect, but any other machine the daemon doesn't even see a connection attempt being made.
  • netcoder
    netcoder about 13 years
    Linux? Windows? If Linux, is SELinux enabled? Routing (e.g.: iptables)?
  • C-speed
    C-speed about 13 years
    netcoder: it's being served on my Macbook, firewall is disabled. I should also clarify that I indeed can connect from other machines via telnet... I simply cannot connection from the browser using the ip or domain name when port 8000 is specified. The daemon doesn't see any connection attempt. If I specify port 6667, then the daemon see's a connection attempt, but that is useless to the user. :(
  • arik
    arik almost 12 years
    @netcoder, just wanted to say how MUCH your answer is appreciated! Thank you sir!