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

I just found out that some of my LWP programs have (inadvertantly) caused a DOS attack on a web site over the last few days. :( The site's netstat command returns a long list of (addresses changed):
tcp 1 0 ::ffff:1.2.3.4:80 ::ffff: 5.6.7.8:33196 CLOSE_WAIT 27647/httpd
Some questions: Does this reuse a connection or keep opening new connections?
#!/usr/bin/perl -w use strict; use LWP::UserAgent; my $ua = LWP::UserAgent->new(agent => "", timeout => 30); my $response; my $url = 'http://example.com/'; while () { sleep 60; $response = $ua->get($url); if (not ($response->is_success)) { print "Can't retrieve page: " . $response->status_line . "\n"; next; } my $page = $response->content; [... process content] }
If it does, a second (similar) program seems like the culprit. I can run it up to twenty times in five minutes, usually with three or four retrievals per instance. The program takes just a few seconds, and then exits completely, but apparently without telling the server to close the connection Does some way exist to close the connection when I exit the program? The second program:
#!/usr/bin/perl -w use strict; use LWP::UserAgent; my $ua = LWP::UserAgent->new(agent => "", timeout => 30); my $url = 'http://example.com/index.php?do=action'; [... some parameter checking] for my $i (@ARGV[2..$#ARGV]) { my $response = $ua->post($url, [ value0 => $ARGV[0], value1 => $ARGV[1], value2 => $i, Submit => 'Submit' ] ); if (not ($response->is_success)) { die "Can't retrieve page: " . $response->status_line . "\n"; + } my $page = $response->content; [... process content] }
I will usually start getting: Can't retrieve page: 500 read timeout errors after a while (while an ordinary browser will continue to access the site without problems). Thank you very much in advance for your suggestions.

Replies are listed 'Best First'.
Re: Closing the connection
by grep (Monsignor) on Oct 20, 2006 at 02:04 UTC
    No your code does not use a persistant connection (most http requests aren't). You would have to set keep_alive => 1 in your LWP::UserAgent call. But that won't work if the server is not setup to handle Keep-Alives.

    But that shouldn't matter, the server connection should be sitting in TIME_WAIT (I've recieved the close and just making sure there are not packets floating around) not CLOSE_WAIT (waiting for client to close the connection).

    My version of LWP dutifully closes the connection running your first example and my server's ports sit in TIME_WAIT for a couple of minutes. Check that you are running a current version of LWP. I would also run tcpdump and check your FIN and ACK responses.

    UPDATE:
    Here is a nice explaination of TIME_WAIT and CLOSE_WAIT.



    grep
    One dead unjugged rabbit fish later
      Thanks for the reply. I upgraded from 5.803 to 5.805 (no difference). I learned a bit more about monitoring this, and (much to my surprise), the first program above didn't cause any problems at all (the connection closes almost immediately). The last part of the tcpdump:
      14:41:12.600732 IP (tos 0x0, ttl 49, id 47053, offset 0, flags [DF], +proto: TCP (6), length: 1460) host.example.com.http > system.localdom +ain.loc.53567: . 85889:87297(1408) ack 174 win 54 <nop,nop,timestamp +1545425010 745586731> 14:41:12.706665 IP (tos 0x0, ttl 49, id 47054, offset 0, flags [DF], +proto: TCP (6), length: 1460) host.example.com.http > system.localdom +ain.loc.53567: . 87297:88705(1408) ack 174 win 54 <nop,nop,timestamp +1545425010 745586731> 14:41:12.706694 IP (tos 0x0, ttl 64, id 25068, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.53567 > host.examp +le.com.http: ., cksum 0xf112 (correct), ack 88705 win 16022 <nop,nop, +timestamp 745589918 1545425010> 14:41:12.812632 IP (tos 0x0, ttl 49, id 47055, offset 0, flags [DF], +proto: TCP (6), length: 1460) host.example.com.http > system.localdom +ain.loc.53567: . 88705:90113(1408) ack 174 win 54 <nop,nop,timestamp +1545425010 745586731> 14:41:12.842084 IP (tos 0x0, ttl 49, id 47056, offset 0, flags [DF], +proto: TCP (6), length: 355) host.example.com.http > system.localdoma +in.loc.53567: FP 90113:90416(303) ack 174 win 54 <nop,nop,timestamp 1 +545425010 745586731> 14:41:12.842123 IP (tos 0x0, ttl 64, id 25069, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.53567 > host.examp +le.com.http: ., cksum 0xe9db (correct), ack 90417 win 16022 <nop,nop, +timestamp 745590053 1545425010> 14:41:12.844363 IP (tos 0x0, ttl 64, id 25070, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.53567 > host.examp +le.com.http: F, cksum 0xe9d7 (correct), 174:174(0) ack 90417 win 1602 +2 <nop,nop,timestamp 745590056 1545425010> 14:41:13.071450 IP (tos 0x0, ttl 49, id 0, offset 0, flags [DF], prot +o: TCP (6), length: 52) host.example.com.http > system.localdomain.lo +c.53567: ., cksum 0x24f8 (correct), ack 175 win 54 <nop,nop,timestamp + 1545425842 745590056>
      The second program above (maybe because of the POST method) causes all of the problems, with each connection staying open for several minutes:
      16:34:08.538882 IP (tos 0x0, ttl 49, id 57175, offset 0, flags [DF], +proto: TCP (6), length: 1460) host.example.com.http > system.localdom +ain.loc.35789: . 9857:11265(1408) ack 291 win 54 <nop,nop,timestamp 1 +547119394 752366263> 16:34:08.538899 IP (tos 0x0, ttl 64, id 63133, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: ., cksum 0x00d5 (correct), ack 11265 win 7252 <nop,nop,t +imestamp 752366807 1547119394> 16:34:08.644817 IP (tos 0x0, ttl 49, id 57176, offset 0, flags [DF], +proto: TCP (6), length: 1460) host.example.com.http > system.localdom +ain.loc.35789: P 11265:12673(1408) ack 291 win 54 <nop,nop,timestamp +1547119394 752366263> 16:34:08.644831 IP (tos 0x0, ttl 64, id 63134, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: ., cksum 0xf816 (correct), ack 12673 win 7976 <nop,nop,t +imestamp 752366913 1547119394> 16:34:08.664134 IP (tos 0x0, ttl 49, id 57177, offset 0, flags [DF], +proto: TCP (6), length: 238) host.example.com.http > system.localdoma +in.loc.35789: P 12673:12859(186) ack 291 win 54 <nop,nop,timestamp 15 +47119423 752366376> 16:34:08.664158 IP (tos 0x0, ttl 64, id 63135, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: ., cksum 0xf46c (correct), ack 12859 win 8680 <nop,nop,t +imestamp 752366932 1547119423> 16:34:08.674181 IP (tos 0x0, ttl 49, id 57178, offset 0, flags [DF], +proto: TCP (6), length: 73) host.example.com.http > system.localdomai +n.loc.35789: P, cksum 0x015b (correct), 12859:12880(21) ack 291 win 5 +4 <nop,nop,timestamp 1547119431 752366376> 16:34:08.674194 IP (tos 0x0, ttl 64, id 63136, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: ., cksum 0xf445 (correct), ack 12880 win 8680 <nop,nop,t +imestamp 752366942 1547119431> 16:34:08.674692 IP (tos 0x0, ttl 64, id 63137, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: F, cksum 0xf443 (correct), 291:291(0) ack 12880 win 8680 + <nop,nop,timestamp 752366943 1547119431> 16:34:08.680820 IP (tos 0x0, ttl 49, id 57179, offset 0, flags [DF], +proto: TCP (6), length: 52) host.example.com.http > system.localdomai +n.loc.35789: F, cksum 0x182d (correct), 12880:12880(0) ack 291 win 54 + <nop,nop,timestamp 1547119431 752366376> 16:34:08.680858 IP (tos 0x0, ttl 64, id 63138, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: ., cksum 0xf43c (correct), ack 12881 win 8680 <nop,nop,t +imestamp 752366949 1547119431> 16:34:09.828456 IP (tos 0x0, ttl 64, id 63139, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: F, cksum 0xefc0 (correct), 291:291(0) ack 12881 win 8680 + <nop,nop,timestamp 752368097 1547119431> 16:34:12.136097 IP (tos 0x0, ttl 64, id 63140, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: F, cksum 0xe6bc (correct), 291:291(0) ack 12881 win 8680 + <nop,nop,timestamp 752370405 1547119431> 16:34:16.751393 IP (tos 0x0, ttl 64, id 63141, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: F, cksum 0xd4b4 (correct), 291:291(0) ack 12881 win 8680 + <nop,nop,timestamp 752375021 1547119431> 16:34:25.981983 IP (tos 0x0, ttl 64, id 63142, offset 0, flags [DF], +proto: TCP (6), length: 52) system.localdomain.loc.35789 > host.examp +le.com.http: F, cksum 0xb0a4 (correct), 291:291(0) ack 12881 win 8680 + <nop,nop,timestamp 752384253 1547119431>
      I can't really decipher everything going on there. If you have any additional insights, please share them. Thank you again for your help.
        The 'F' flag after the destination (host.example.com.http:) is a FIN. So LWP appears to be sending a FIN in both cases.

        At this point, to rule out any problem on your part, I would follow tye's suggestion. Setup your script(s) to do 1 iteration. Then cron (or windows schedule) them to run at your intervals.

        Send me an email and I'll have you run this againnst my server. I can watch the ports from there.



        grep
        One dead unjugged rabbit fish later
Re: Closing the connection (shutdown)
by tye (Sage) on Oct 20, 2006 at 06:00 UTC

    It appears that some network stacks (Win32's, it appears) can fail to properly close a connection when the socket is closed (such as when the process exits). Calling shutdown then close is the most sure way to fully and properly close the socket.

    You could try adding the following to your script so that an orderly exit will explicitly close any sockets, including calling shutdown:

    use Socket; sub IO::Socket::DESTROY { my $sock= shift @_; return if ! defined fileno $sock; my $peerAddr= getpeername( $sock ); my( $port, $ip )= sockaddr_in( $peerAddr ); my $host= gethostbyaddr( $ip, AF_INET() ); $ip= inet_ntoa( $ip ); $ip= "$host($ip)" if $host; warn "Shutting down socket to $ip:$port...\n"; shutdown( $sock, 2 ); close( $sock ); }

    which I tested against a simplified version of your script fragment.

    - tye