http://qs1969.pair.com?node_id=51126

zigster has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to write a process that essentially acts as an conduit between two other proceses. I want to open a serversocket A and a client socket B. Something like.
$server = IO::Socket::INET->new( Listen => 5, LocalAddr => 'localhost', LocalPort => $local_port, Proto => 'tcp') or die "Cant create server socket: !"; while ($client = $server->accept) { $proxy = IO::Socket::INET->new( PeerAddr => $proxy_host, PeerPort => $proxy_port, Proto => 'tcp') or die "cannot create proxy socket: $!" # At this point here I want to connect the $proxy file handle and # the $client file handle .. }
By connect I mean I want the any op from $client to be shoved into the ip of $proxy and any op from $proxy to be shoved in the ip of $client. Clear .. no not to me either I am sure I am doing summat wrong.

Now I guess I could do this by spawning 2 threads one to read and one to write then just bucket the data across however I am looking for a simpler solution.

I would be keen to hear anyones suggestions. I half think I am missing some thing and should not be even attempting this so please tell me I am being dumb if you see that to be the case.

Thanks very much for your time.
--

Zigster

Replies are listed 'Best First'.
Re: Is there any way to connect two sockets together?
by c-era (Curate) on Jan 11, 2001 at 20:30 UTC
    Here is a simple solution using select (although it will only process one connection at a time). I added comments to the code that I added. You may want to add more error checking.
    use IO::Socket; use IO::Select; $local_port = 5000; $proxy_host = "localhost"; $proxy_port = 6000; $server = IO::Socket::INET->new( Listen => 5, LocalAddr => 'localhost', LocalPort => $local_port, Proto => 'tcp') or die "Cant create server socket: !"; # Create a select object $read = new IO::Select; while ($client = $server->accept) { $proxy = IO::Socket::INET->new( PeerAddr => $proxy_host, PeerPort => $proxy_port, Proto => 'tcp') or die "cannot create proxy socket: $!"; # Unbuffer our output $oldfh = select ($proxy); $| = 1; select ($client); $| = 1; select ($oldfh); # Add our filehandles to our select object $read->add($client); $read->add($proxy); # This will wait until one of the filehandles has something to read while (defined (($readable) = IO::Select->select($read,undef , und +ef, 0))){ foreach $fh (@$readable) { # The client has something to say if ($fh == $client) { $buf = <$client>; if ($buf){ # Lets tell the proxy print $proxy $buf; } else { # The client closed last; } # The proxy has something to say } else { $buf = <$proxy>; if ($buf){ # Lets tell the client print $client $buf; } else { # The proxy closed last; } } } } # Remove our file handles from select and close them $read->remove($client); $read->remove($proxy); $client->close; $proxy->close; } # Now we go onto the next client
    UPDATE: Below is a piece of code that will handle multiple clients, has use strict, and $|=1 taken off.
    use strict; use IO::Socket; use IO::Select; my $local_port = 5000; my $proxy_host = "localhost"; my $proxy_port = 6000; # Used to store client and proxy connections my %client_proxy; my $server = IO::Socket::INET->new( Listen => 5, LocalAddr => 'localhost', LocalPort => $local_port, Proto => 'tcp') or die "Cant create server socket: !"; # Create a select object my $read = new IO::Select; $read->add($server); while (1){ my ($readable) = IO::Select->select($read, undef, undef, 0); foreach my $fh (@$readable){ if ($fh == $server){ # We got a new client, so set up it's configuration my $client = $server->accept; my $proxy = IO::Socket::INET->new( PeerAddr => $proxy_host, PeerPort => $proxy_port, Proto => 'tcp') or die "cannot create proxy socket: $!"; # You probabl +y don't really want to die here. $read->add($client); $read->add($proxy); $client_proxy{$client} = $proxy; $client_proxy{$proxy} = $client; } else { my $buf = <$fh>; if ($buf){ # We need to send some information my $fh2 = $client_proxy{$fh}; print $fh2 $buf; } else { # We need to close our connections my $fh2 = $client_proxy{$fh}; $read->remove($fh2); close ($fh2); delete $client_proxy{$fh2}; $read->remove($fh); close ($fh); delete $client_proxy{$fh}; } } } }
    <SARCASIM>Some people will pick apart the above code because they are jealous that they could write it ; )</SARCASIM>
      Note that IO::Socket file handles are by default set with 'autoflush' enabled, so it's not necessary to do that here, especially not by using the select/$|=1/select method. We are dealing with objects, you know. :) $client->autoflush(1)

      In addition, you might be interested in the 'can_read' method of IO::Select instead of calling select directly. This is a little "friendlier":

      my @readable = $read->can_read; foreach $fh (@readable) { ... }
        I forgot about the autoflush. As for the can_read method, it dosen't work that great on active perl, but the regular select works fine.
(tye)Re: Is there any way to connect two sockets together?
by tye (Sage) on Jan 11, 2001 at 20:09 UTC

    I don't think there is a simpler way (well, unless you consider using select simpler than threads). I wrote such a beast and it works quite nicely for me. There is also one in the Perl Cookbook (if you have a copy or know where to download the code samples -- which I don't, sorry).

    Update: I think this is called a "port mapper" in the Cookbook.

            - tye (but my friends call me "Tye")
      Thanks for the hint, I was playing with select as I read your post ;-) however I was not able to find any references in the cookbook. I had previously looked do you have any idea what section it was it?
      --

      Zigster
        Looks like it's section 17.18, "Program: fwdport".
Re: Is there any way to connect two sockets together?
by zigster (Hermit) on Jan 11, 2001 at 21:30 UTC
    Thanks HUGELY to everyone who has helped me here for the code and the advice. I have all but a working system now.. Thanks guys (and gals).
    --

    Zigster
Re: Is there any way to connect two sockets together?
by jeroenes (Priest) on Jan 11, 2001 at 20:42 UTC
    Why don't you just use SSH for that?
    ssh localhost -f -S -L $localport:$proxy:$proxyport
    and it does exactly what your script is trying to do. But I'm still confused if that is what you want to do, as I see little use for it.

    Cheers,

    Jeroen
    I was dreaming of guitarnotes that would irritate an executive kind of guy (FZ)

    Update: The -S option of SSH makes sure it doesn't try to connect, only functions as portmapper. Moreover, I forgot the supply 'localhost', and the -f option is handy as well.

      Because my script is a cut down of the big picture. In the big picture it tunnels over a http proxy. It opens up a socket connection with a remote internet host via the proxy (using the connect method) this is a trival negotiation1. It then connects the data recieved from that connection to a local port. I can then connect applications to the local port and so to the outside world.

      1 CONNECT host:port HTTP1/0 <NL> or some such.
      --

      Zigster

        Aha.

        Than use the SSH solution I gave you should work. It just tunnels a local connection to $localport to $proxy:$proxyport. You simply let your client connect to $localport and it wouldn't know the difference.

        Hope this helps,

        Jeroen
        I was dreaming of guitarnotes that would irritate an executive kind of guy (FZ)