Hey, this is my first time trying to help someone out, but i believe i know the solution to your problem. Instead of using the $sock->print() method, why not try using $sock as a filehandle. Like this:
my $sock = IO::Socket::INET->new(PeerAddr => $host,
PeerPort => $port,
Proto => $proto);
my $request;
if ($port == 21) {
$request = "blah";
} elsif ($port == 22) {
$request = "\012\012";
} elsif ($port == 80) {
$request = "HEAD / HTTP/1.0\012\012";
}
print $sock $request;
while (<$sock>) {
chomp (my $server = $_);
print $server;
}
close($sock);
its slightly different from your code, but its easier to understand. I am not completely sure why you have to put the print method outside of the while loop, but i'm pretty sure it has to do with the fact that when you use while to read from a filehandle, it does it line by line (line defined as whatever perl dereferences long strings, set by $/ and defaults to \n). If your head reponse wasn't finished before the newline was, then you are keeping the same socket open, then reopening it which is usually a bad thing, either way i'm still an amateur and i'm sure someone probably has a way better explanation. | [reply] [d/l] |
Hi chrono86
glad to be a part of your first post :). I do like what you've done here, but it's a reformulation of the
same problem I am seeing, e.g. print $sock $request; will work for port 80 requests but not for port 21 or
22 request. On the otherhand, if I place print $sock $request; inside the while loop then banners are
returned for port 21 and 22 but not for port 80. Try it out and let me know if you see otherwise. Thanks
for the feedback.
cheers, -semio
| [reply] |
After a long time of testing and retesting and trying to figure out what was wrong with my stupid ftp server (on port 21, hehe) i finally figured out the answer to your problem. It's unbelievably simple too. The most basic rule behind opening socket connections to servers is that you must read input before you send any request to the server. The first mistake in the script is that you forget to read the introduction message from the ftp server before you print that request, since most ftp servers respond to a connection witha simple "welcome" message, while HTTP servers don't. The second mistake here is that when you check for output from the server when there is none, perl waits on the server until it sends a null message or message with data. so if you do:
if (<$sock>) {
print;
}
and the server doesn't have any response to give to you, perl will hang, since it is waiting for the yes or no response from the server. The same goes for while:
while (<$sock>) {
print;
}
it will print the first the introductory lines from the ftp server, but then it will hang waiting to see if the third one exists. thats why when you do:
while (<$sock>) {
print $sock $request;
print;
}
it works on the ftp server because you are constantly giving the server a request to respond to, therefore it will always be responding and perl won't be hanging when it checks the socket. I hope that answers your question, it sure did clear up tons of things for me. when perl does a while loop for input on the http server, the while loop works because an http server closes the connection after one request, so if it checks to see if the socket still exists, perl immediately knows it doesn't since the connection is closed and safely breaks the while loop. With this in mind it probably isn't smart to use a while loop with a server that keeps connections alive until you close them. i made an alternative to your program to work with different types of known servers. check it out:
use strict;
use IO::Socket;
#initial variables to work with my server
my $host, $port, $request, $proto = 'tcp';
my $connectresponses = 2; #my ftp server responds with 2 lines when yo
+u connect.
print "What hostname do you want to connect to? ";
chomp($host = <STDIN>);
print "What port do you want to use? ";
chomp($port = <STDIN>);
my $sock = IO::Socket::INET->new(PeerAddr => $host,
PeerPort => $port,
Proto => $proto) || die "Failure: $!";
print "Connection to $host on port $port successful!\n";
unless ($port == 80) {
for ($i = 0; $i < $connectresponses; $i++) {
$_ = <$sock>;
print;
}
}
print "Type commands (solely press enter to exit)\n";
&Terminal;
close($sock);
print "\nConnection to $host on port $port was closed successfully!\n"
+;
exit;
#sub to emulate a terminal
sub Terminal {
while (1) {
$request = "";
chomp($request = <STDIN>);
print $sock "$request\n";
if ($port == 80) {
while (<$sock>) {
print;
}
last;
} else {
unless ($request) {
last;
}
$_ = <$sock>;
print;
}
}
}
I know it has a few bugs, like when the non-port 80 server reponds with more than one line of response. I got this far, but i am not sure if there is anyway to check how many lines of output the server has to offer.
_____________ALSO______________
These same rules apply for any FILEHANDLE, as i have discovered. So if there is nothing to read, perl will always hang. If there were a way to check that i could make that program so much better, i hear you can use the select function to check what a filehandle has to offer but i haven't figured it out yet. hehe Who needs telnet anyway? heheh. anyways i hope this finally answers your question :)
-rian | [reply] [d/l] [select] |
It's quite logical that you have to place the $sock->print() outside of the while loop for two reasons:
- First you do the request, THEN you get an answer
- <$sock> will not have anything before you do the print on $socj so the while (<$sock>) will exit immediately if you did not do the print before it.
| [reply] |
Hi Jaap
It's quite logical that you have to place the $sock->print() outside of the while loop
If you try this, I think you'll see that banners will not be returned for ports 21 or 22. This was the basis for my original question, e.g. why I'm seeing different behavior on a port by port basis based on where I place the $sock->print() statement. Thanks for the feedback though.
cheers, -semio
| [reply] |