in reply to Re: Thread weirdness
in thread Thread weirdness

It's an IRC server not a web server. The thread function is not doing any recving.

Replies are listed 'Best First'.
Re^3: Thread weirdness
by zentara (Cardinal) on Sep 25, 2008 at 11:37 UTC
    t's an IRC server not a web server.

    In your original node you identified the problem as: One of the functions connects to a web page and parses the content

    So it dosn't matter if you are an IRC bot, it is still trying to access a web server thru a socket, so you need to deal with the http protocols. This is the most basic ....notice you need to send a Get, or at least a "\n\n", before the web server will respond.

    #!/usr/bin/perl -w # Usage: this_script 192.168.0.1 /~zentara/index.html use IO::Socket; unless (@ARGV > 1) { die "usage: $0 host document ..." } $host = shift(@ARGV); foreach $document ( @ARGV ) { $remote = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => "http(80)", ); unless ($remote) { die "cannot connect to http daemon on $host" } $remote->autoflush(1); print $remote "GET $document HTTP/1.0\n\n"; while ( <$remote> ) { print } close $remote; }

    I'm not really a human, but I play one on earth Remember How Lucky You Are
      Here is what an example session looks like:
      * botbot (Botty@rox-AFBE0982) has joined #wtf
      Right now I private message it to login:
      <me> .login 123456 <botbot> Password correct, logged in
      Now I go back to the channel:
      <me> .web http://www.google.com <botbot> WEB FUNCTION THREAD STARTED
      I wait about five seconds for it to send the title of google.com back to me, it doesn't send so i just send some random text.
      <me> ZZZZZZZZZZZZZZZZZZZZ <botbot> Got title: Google <botbot> WEB FUNCTION THREAD FINISHED
      Here's the full bot
      #!/usr/bin/perl use strict; use threads; use IO::Socket::INET; use LWP::UserAgent; my $ua = LWP::UserAgent->new(agent => 'Mozilla/5.0 (Windows; U; Window +s NT 5.1; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.5'); push @{$ua->requests_redirectable}, "POST"; my($Sock,$Line,$Nick); my(@IRCUnparsed,@IRCParsed); my(%BossUsers); ############################################ my $Server = $ARGV[0] || "127.0.0.1"; my $Channel = $ARGV[1] || qw(#wtf); my $Port = $ARGV[2] || qw(6667); my $NickName = "botbot"; my $Password = qw(123456); ############################################ $Sock = new IO::Socket::INET(PeerAddr=>"$Server:$Port",Proto=>"tcp"); $Sock->autoflush(); die("Couldn't connect: $!\n") unless $Sock; &SendNick(); &send_raw("USER Botty Bot Bott :Bot bot"); while(1){ $Line = <$Sock>; if(!$Sock){print("[-] Sock died\n");exit(0);} @IRCUnparsed = split(/\r\n/,&tchomp($Line)); @IRCParsed = split(/ /,$IRCUnparsed[0]); print($IRCUnparsed[0] . "\n"); if($IRCParsed[0] eq 'PING'){ &send_raw("PONG " . $IRCParsed[1]); } elsif($IRCParsed[1] eq '001'){ &JoinChan; } elsif(($IRCParsed[1] eq '433') || ($IRCParsed[1] eq "436")){ #&SendNick(); #&JoinChan(); } elsif($IRCParsed[1] eq '475'){ &send_raw("QUIT"); } elsif($IRCParsed[1] eq 'KICK'){ &JoinChan; } if(($IRCUnparsed[0] =~ m/^:(.+)!(.+)\@(.+) PRIVMSG (.+) :(.+)/) || + ($IRCUnparsed[0] =~ m/^:(.+)!~(.+)\@(.+) PRIVMSG (.+) :/)){ my($uNick,$uID,$uHost) = ($1,$2,$3); if($5 =~ m/quit/){ &send_raw("QUIT"); die(); } if($4 eq $Channel){ if($IRCParsed[3] eq ':.web'){ my $URL = $IRCParsed[4]; my $Thr = threads->new(\&Web,$URL); } #chan messages } elsif($4 eq $Nick){ #private messages if($BossUsers{$uNick} == 0){ for(0..@IRCParsed){ #print($_ . " -> " . $IRCParsed[$_] . "\n"); } if($IRCParsed[3] eq ':.login'){ if($IRCParsed[4] eq $Password){ &send_msg($uNick,"Password correct, logged in" +); $BossUsers{$uNick} = 1; } else{ &send_msg($uNick,"Password incorrect"); $BossUsers{$uNick} = 0; } } } elsif($BossUsers{$uNick} => 1){ if($IRCParsed[3] eq ':.logout'){ $BossUsers{$uNick} = 0; &send_msg($uNick,"Logged out"); } elsif($IRCParsed[3] eq ":.quit"){ &SendQuit(); } } } $IRCParsed[0] = $1; } } sub Web($) { &send_msg($Channel,"WEB FUNCTION THREAD STARTED"); my($URL,$Request,$Requested,$Content) = @_; $Request = HTTP::Request->new(GET => $URL); $Requested = $ua->request($Request); if($Requested->is_success){ $Content = $Requested->content; if($Content =~ m/<title>(.+)<\/title>/){ &send_msg($Channel,"Got title: $1"); } else{ &send_msg($Channel,"Didn't get title"); } } else{ print "\n[-] Connection error - " . $Requested->status_line . +"\n"; } &send_msg($Channel,"WEB FUNCTION THREAD FINISHED"); } sub SendQuit() { &send_raw("QUIT"); } sub JoinChan() { &send_raw("JOIN " . $Channel); } sub SendNick() { #$Nick = &randnick($NickPrefix); $Nick = $NickName; &send_raw("NICK " . $Nick); } sub randnick($){ return $_[0] . int(rand(1)*100) . int(rand(1)*200) . int(rand(1)*3 +00); } sub send_raw($){ print "[+] $_[0]\n"; print $Sock "$_[0]\r\n"; } sub send_msg($$){ print "[+] PRIVMSG $_[0] :$_[1]\n"; print $Sock "PRIVMSG $_[0] :$_[1]\r\n"; } sub tchomp { my $text = shift; $text =~ s/^(.*?)(?:\x0D\x0A|\x0A|\x0D|\x0C|\x{2028}|\x{2029})/$1/ +s; return $text; } sub urlencode($){ my $unclean = shift; $unclean =~ s/\?/\%3f/gi; $unclean =~ s/ /\+/gi; $unclean =~ s/:/\%3A/gi; $unclean =~ s/\//\%2F/gi; $unclean =~ s/&/\%26/gi; $unclean =~ s/\"/\%22/gi; $unclean =~ s/\'/\%27/gi; $unclean =~ s/,/\%2C/gi; $unclean =~ s/\\/\%5C/gi; return $unclean; } sub urldecode($){ my $clean = shift; $clean =~ s/\%3f/\?/gi; $clean =~ s/\+/ /gi; $clean =~ s/\%3A/:/gi; $clean =~ s/\%2F/\//gi; $clean =~ s/\%26/&/gi; $clean =~ s/\%22/\"/gi; $clean =~ s/\%27/\'/gi; $clean =~ s/\%2C/,/gi; $clean =~ s/\%5C/\\/gi; return $clean; }

        I've looked through the code you (finally) posted. I think at least part of the problem is that $Sock is not declared as shared and you will thus have problems with potential race conditions, at least as far as I understand the Perl threading model. I would restrict all communication within your program to Thread::Queues and have one (and only one) thread specialized to talking to the server and listening to replies. That is, I would turn your send_msg function into one that stuffs the message into a queue, and at the other end of that queue have a thread that does nothing but listen to that queue and forward the things to the server.

        Ideally, no background process would do direct communication with the irc server, they would all deliver their results to the server thread which then handles the communication. You will need send rate limiting anyway so your bot does not get kicked off the server for flooding it.

        Personally, I would look towards AnyEvent::IRC or any of the other IRC frameworks though.

        Caveat: Most of the contents of this post will only relate to you if you are using Win32. (Although the first bit about not being able to send on a socket from one thread whilst another thread is attempting to read from it may also be true on *nix for blocking sockets?)

        The problem is that your are trying to both read and write to $Sock concurrently from two threads:

        In your main thread:

        while(1){ $Line = <$Sock>;

        And in your Web thread, several instances of: &send_msg($Channel,"WEB FUNCTION THREAD STARTED"); which becomes:

        print $Sock "PRIVMSG $_[0] :$_[1]\r\n";

        As your threads are blocking, the writes SendMsg() is blocked until the read in the main thread completes. (Ie. some input arrives).

        One solution is to set your socket non-blocking. (If your using Win32 Super search for '0x8004667e' for the collective wisdom on Win32 non-blocking sockets). However, this has a fairly profound knock-on for the architecture of your app.

        A better solution is to only enter a read state when you know that there is something to be read. Unfortunately, the Win32 API that permits this is not directly available from Perl. Even if you access it directly (via: Win32::API), obtaining the appropriate Win32 system handle required to use it, , from the Perl level sockets, is entirely non-trivial.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.