ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/DNS.pm
(Generate patch)

Comparing AnyEvent/lib/AnyEvent/DNS.pm (file contents):
Revision 1.8 by root, Fri May 23 05:34:32 2008 UTC vs.
Revision 1.19 by root, Fri May 23 23:37:13 2008 UTC

9=head1 DESCRIPTION 9=head1 DESCRIPTION
10 10
11This module offers both a number of DNS convenience functions as well 11This module offers both a number of DNS convenience functions as well
12as a fully asynchronous and high-performance pure-perl stub resolver. 12as a fully asynchronous and high-performance pure-perl stub resolver.
13 13
14The stub resolver supports DNS over UDP, optional EDNS0 support for up to
154kiB datagrams and automatically falls back to virtual circuit mode for
16large responses.
17
14=head2 CONVENIENCE FUNCTIONS 18=head2 CONVENIENCE FUNCTIONS
15 19
16=over 4 20=over 4
17 21
18=cut 22=cut
20package AnyEvent::DNS; 24package AnyEvent::DNS;
21 25
22no warnings; 26no warnings;
23use strict; 27use strict;
24 28
25use AnyEvent::Util ();
26use AnyEvent::Handle (); 29use AnyEvent::Handle ();
27 30
28=item AnyEvent::DNS::addr $node, $service, $family, $type, $cb->(@addrs) 31=item AnyEvent::DNS::addr $node, $service, $proto, $family, $type, $cb->([$family, $type, $proto, $sockaddr], ...)
29 32
30NOT YET IMPLEMENTED 33NOT YET IMPLEMENTED
31 34
32Tries to resolve the given nodename and service name into sockaddr 35Tries to resolve the given nodename and service name into protocol families
33structures usable to connect to this node and service in a 36and sockaddr structures usable to connect to this node and service in a
34protocol-independent way. It works similarly to the getaddrinfo posix 37protocol-independent way. It works remotely similar to the getaddrinfo
35function. 38posix function.
39
40C<$node> is either an IPv4 or IPv6 address or a hostname, C<$service> is
41either a service name (port name from F</etc/services>) or a numerical
42port number. If both C<$node> and C<$service> are names, then SRV records
43will be consulted to find the real service, otherwise they will be
44used as-is. If you know that the service name is not in your services
45database, then you cna specify the service in the format C<name=port>
46(e.g. C<http=80>).
47
48C<$proto> must be a protocol name, currently C<tcp>, C<udp> or
49C<sctp>. The default is C<tcp>.
50
51C<$family> must be either C<0> (meaning any protocol is ok), C<4> (use
52only IPv4) or C<6> (use only IPv6).
53
54C<$type> must be C<SOCK_STREAM>, C<SOCK_DGRAM> or C<SOCK_SEQPACKET> (or
55C<undef> in which case it gets automatically chosen).
56
57The callback will receive zero or more array references that contain
58C<$family, $type, $proto> for use in C<socket> and a binary
59C<$sockaddr> for use in C<connect> (or C<bind>).
60
61The application should try these in the order given.
36 62
37Example: 63Example:
38 64
39 AnyEvent::DNS::addr "google.com", "http", AF_UNSPEC, SOCK_STREAM, sub { ... }; 65 AnyEvent::DNS::addr "google.com", "http", 0, undef, undef, sub { ... };
40 66
41=item AnyEvent::DNS::a $domain, $cb->(@addrs) 67=item AnyEvent::DNS::a $domain, $cb->(@addrs)
42 68
43Tries to resolve the given domain to IPv4 address(es). 69Tries to resolve the given domain to IPv4 address(es).
70
71=item AnyEvent::DNS::aaaa $domain, $cb->(@addrs)
72
73Tries to resolve the given domain to IPv6 address(es).
44 74
45=item AnyEvent::DNS::mx $domain, $cb->(@hostnames) 75=item AnyEvent::DNS::mx $domain, $cb->(@hostnames)
46 76
47Tries to resolve the given domain into a sorted (lower preference value 77Tries to resolve the given domain into a sorted (lower preference value
48first) list of domain names. 78first) list of domain names.
74=item AnyEvent::DNS::ptr $ipv4_or_6, $cb->(@hostnames) 104=item AnyEvent::DNS::ptr $ipv4_or_6, $cb->(@hostnames)
75 105
76Tries to reverse-resolve the given IPv4 or IPv6 address (in textual form) 106Tries to reverse-resolve the given IPv4 or IPv6 address (in textual form)
77into it's hostname(s). 107into it's hostname(s).
78 108
79Requires the Socket6 module for IPv6 support.
80
81Example: 109Example:
82 110
83 AnyEvent::DNS::ptr "2001:500:2f::f", sub { print shift }; 111 AnyEvent::DNS::ptr "2001:500:2f::f", sub { print shift };
84 # => f.root-servers.net 112 # => f.root-servers.net
85 113
94 122
95sub a($$) { 123sub a($$) {
96 my ($domain, $cb) = @_; 124 my ($domain, $cb) = @_;
97 125
98 resolver->resolve ($domain => "a", sub { 126 resolver->resolve ($domain => "a", sub {
127 $cb->(map $_->[3], @_);
128 });
129}
130
131sub aaaa($$) {
132 my ($domain, $cb) = @_;
133
134 resolver->resolve ($domain => "aaaa", sub {
99 $cb->(map $_->[3], @_); 135 $cb->(map $_->[3], @_);
100 }); 136 });
101} 137}
102 138
103sub mx($$) { 139sub mx($$) {
134} 170}
135 171
136sub ptr($$) { 172sub ptr($$) {
137 my ($ip, $cb) = @_; 173 my ($ip, $cb) = @_;
138 174
139 my $name; 175 $ip = AnyEvent::Socket::parse_ip ($ip)
176 or return $cb->();
140 177
141 if (AnyEvent::Util::dotted_quad $ip) { 178 if (4 == length $ip) {
142 $name = join ".", (reverse split /\./, $ip), "in-addr.arpa."; 179 $ip = join ".", (reverse split /\./, $ip), "in-addr.arpa.";
143 } else { 180 } else {
144 require Socket6; 181 $ip = join ".", (reverse split //, unpack "H*", $ip), "ip6.arpa.";
145 $name = join ".",
146 (reverse split //,
147 unpack "H*", Socket6::inet_pton (Socket::AF_INET6, $ip)),
148 "ip6.arpa.";
149 } 182 }
150 183
151 resolver->resolve ($name => "ptr", sub { 184 resolver->resolve ($ip => "ptr", sub {
152 $cb->(map $_->[3], @_); 185 $cb->(map $_->[3], @_);
153 }); 186 });
154} 187}
155 188
156sub any($$) { 189sub any($$) {
157 my ($domain, $cb) = @_; 190 my ($domain, $cb) = @_;
158 191
159 resolver->resolve ($domain => "*", $cb); 192 resolver->resolve ($domain => "*", $cb);
160} 193}
161 194
195#############################################################################
196
197#AnyEvent::DNS::addr $node, $service, $family, $type, $proto, $cb->([$family, $type, $protocol, $sockaddr], ...)
198
199# $port, $host
200sub pack_sockaddr_in6($$) {
201 pack "nnN a16 N",
202 Socket::AF_INET6,
203 $_[0], # port
204 0, # flowinfo
205 $_[1], # addr
206 0 # scope id
207}
208
209sub addr($$$$$$) {
210 my ($node, $service, $proto, $family, $type, $cb) = @_;
211
212 unless (eval { &Socket::AF_INET6 }) {
213 $family != 6
214 or return $cb->();
215
216 $family ||= 4;
217 }
218
219 $proto ||= "tcp";
220 $type ||= $proto eq "udp" ? Socket::SOCK_DGRAM : Socket::SOCK_STREAM;
221
222 my $proton = (getprotobyname $proto)[2]
223 or Carp::croak "$proto: protocol unknown";
224
225 my $port;
226
227 if ($service =~ /^(\S+)=(\d+)$/) {
228 ($service, $port) = ($1, $2);
229 } elsif ($service =~ /^\d+$/) {
230 ($service, $port) = (undef, $service);
231 } else {
232 $port = (getservbyname $service, $proto)[2]
233 or Carp::croak "$service/$proto: service unknown";
234 }
235
236 my @target = [$node, $port];
237
238 # resolve a records / provide sockaddr structures
239 my $resolve = sub {
240 my @res;
241 my $cv = AnyEvent->condvar (cb => sub {
242 $cb->(map $_->[1], sort { $a->[0] <=> $b->[0] } @res)
243 });
244
245 $cv->begin;
246 for my $idx (0 .. $#target) {
247 my ($node, $port) = @{ $target[$idx] };
248
249 if (my $noden = AnyEvent::Socket::parse_ip ($node)) {
250 if (4 == length $noden && $family != 6) {
251 push @res, [$idx, [Socket::AF_INET, $type, $proton,
252 Socket::pack_sockaddr_in $port, $noden]]
253 }
254
255 if (16 == length $noden && $family != 4) {
256 push @res, [$idx, [Socket::AF_INET6, $type, $proton,
257 pack_sockaddr_in6 $port, $noden]]
258 }
259 } else {
260 # ipv4
261 if ($family != 6) {
262 $cv->begin;
263 a $node, sub {
264 push @res, [$idx, [Socket::AF_INET, $type, $proton,
265 Socket::pack_sockaddr_in $port, AnyEvent::Socket::parse_ipv4 ($_)]]
266 for @_;
267 $cv->end;
268 };
269 }
270
271 my $idx = $idx + 0.5; # prefer ipv4 for now
272
273 # ipv6
274 if ($family != 4) {
275 $cv->begin;
276 aaaa $node, sub {
277 push @res, [$idx, [Socket::AF_INET6, $type, $proton,
278 pack_sockaddr_in6 $port, AnyEvent::Socket::parse_ipv6 ($_)]]
279 for @_;
280 $cv->end;
281 };
282 }
283 }
284 }
285 $cv->end;
286 };
287
288 # try srv records, if applicable
289 if (defined $service && !AnyEvent::Socket::parse_ip ($node)) {
290 srv $service, $proto, $node, sub {
291 my (@srv) = @_;
292
293 # no srv records, continue traditionally
294 @srv
295 or return &$resolve;
296
297 # only srv record has "." => abort
298 $srv[0][2] ne "." || $#srv
299 or return $cb->();
300
301 # use srv records then
302 @target = map [$_->[3], $_->[2]],
303 grep $_->[3] ne ".",
304 @srv;
305
306 &$resolve;
307 };
308 } else {
309 &$resolve;
310 }
311}
312
313#############################################################################
314
315=back
316
162=head2 DNS EN-/DECODING FUNCTIONS 317=head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS
163 318
164=over 4 319=over 4
165 320
321=item $AnyEvent::DNS::EDNS0
322
323This variable decides whether dns_pack automatically enables EDNS0
324support. By default, this is disabled (C<0>), but when set to C<1>,
325AnyEvent::DNS will use EDNS0 in all requests.
326
166=cut 327=cut
328
329our $EDNS0 = 0; # set to 1 to enable (partial) edns0
167 330
168our %opcode_id = ( 331our %opcode_id = (
169 query => 0, 332 query => 0,
170 iquery => 1, 333 iquery => 1,
171 status => 2, 334 status => 2,
309 + $rcode_id{$req->{rc}} * 0x0001, 472 + $rcode_id{$req->{rc}} * 0x0001,
310 473
311 scalar @{ $req->{qd} || [] }, 474 scalar @{ $req->{qd} || [] },
312 scalar @{ $req->{an} || [] }, 475 scalar @{ $req->{an} || [] },
313 scalar @{ $req->{ns} || [] }, 476 scalar @{ $req->{ns} || [] },
314 scalar @{ $req->{ar} || [] }, # include EDNS0 option here 477 $EDNS0 + scalar @{ $req->{ar} || [] }, # include EDNS0 option here
315 478
316 (join "", map _enc_qd, @{ $req->{qd} || [] }), 479 (join "", map _enc_qd, @{ $req->{qd} || [] }),
317 (join "", map _enc_rr, @{ $req->{an} || [] }), 480 (join "", map _enc_rr, @{ $req->{an} || [] }),
318 (join "", map _enc_rr, @{ $req->{ns} || [] }), 481 (join "", map _enc_rr, @{ $req->{ns} || [] }),
319 (join "", map _enc_rr, @{ $req->{ar} || [] }), 482 (join "", map _enc_rr, @{ $req->{ar} || [] }),
320 483
321 # (pack "C nnNn", 0, 41, 4000, 0, 0) # EDNS0, 4k udp payload size 484 ($EDNS0 ? pack "C nnNn", 0, 41, 4096, 0, 0 : "") # EDNS0, 4kiB udp payload size
322} 485}
323 486
324our $ofs; 487our $ofs;
325our $pkt; 488our $pkt;
326 489
355 my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4; 518 my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4;
356 [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc] 519 [$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc]
357} 520}
358 521
359our %dec_rr = ( 522our %dec_rr = (
360 1 => sub { Socket::inet_ntoa $_ }, # a 523 1 => sub { join ".", unpack "C4" }, # a
361 2 => sub { local $ofs = $ofs - length; _dec_qname }, # ns 524 2 => sub { local $ofs = $ofs - length; _dec_qname }, # ns
362 5 => sub { local $ofs = $ofs - length; _dec_qname }, # cname 525 5 => sub { local $ofs = $ofs - length; _dec_qname }, # cname
363 6 => sub { 526 6 => sub {
364 local $ofs = $ofs - length; 527 local $ofs = $ofs - length;
365 my $mname = _dec_qname; 528 my $mname = _dec_qname;
366 my $rname = _dec_qname; 529 my $rname = _dec_qname;
367 ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs) 530 ($mname, $rname, unpack "NNNNN", substr $pkt, $ofs)
368 }, # soa 531 }, # soa
369 11 => sub { ((Socket::inet_aton substr $_, 0, 4), unpack "C a*", substr $_, 4) }, # wks 532 11 => sub { ((join ".", unpack "C4"), unpack "C a*", substr $_, 4) }, # wks
370 12 => sub { local $ofs = $ofs - length; _dec_qname }, # ptr 533 12 => sub { local $ofs = $ofs - length; _dec_qname }, # ptr
371 13 => sub { unpack "C/a C/a", $_ }, # hinfo 534 13 => sub { unpack "C/a C/a", $_ }, # hinfo
372 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_qname) }, # mx 535 15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_qname) }, # mx
373 16 => sub { unpack "(C/a)*", $_ }, # txt 536 16 => sub { unpack "(C/a)*", $_ }, # txt
374 28 => sub { sprintf "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", unpack "n8" }, # aaaa 537 28 => sub { AnyEvent::Socket::format_ip ($_) }, # aaaa
375 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_qname) }, # srv 538 33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_qname) }, # srv
376 99 => sub { unpack "(C/a)*", $_ }, # spf 539 99 => sub { unpack "(C/a)*", $_ }, # spf
377); 540);
378 541
379sub _dec_rr { 542sub _dec_rr {
394 557
395Unpacks a DNS packet into a perl data structure. 558Unpacks a DNS packet into a perl data structure.
396 559
397Examples: 560Examples:
398 561
399 # a non-successful reply 562 # an unsuccessful reply
400 { 563 {
401 'qd' => [ 564 'qd' => [
402 [ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ] 565 [ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ]
403 ], 566 ],
404 'rc' => 'nxdomain', 567 'rc' => 'nxdomain',
408 'uni-karlsruhe.de', 571 'uni-karlsruhe.de',
409 'soa', 572 'soa',
410 'in', 573 'in',
411 'netserv.rz.uni-karlsruhe.de', 574 'netserv.rz.uni-karlsruhe.de',
412 'hostmaster.rz.uni-karlsruhe.de', 575 'hostmaster.rz.uni-karlsruhe.de',
413 2008052201, 576 2008052201, 10800, 1800, 2592000, 86400
414 10800,
415 1800,
416 2592000,
417 86400
418 ] 577 ]
419 ], 578 ],
420 'tc' => '', 579 'tc' => '',
421 'ra' => 1, 580 'ra' => 1,
422 'qr' => 1, 581 'qr' => 1,
488 647
489=back 648=back
490 649
491=head2 THE AnyEvent::DNS RESOLVER CLASS 650=head2 THE AnyEvent::DNS RESOLVER CLASS
492 651
493This is the class which deos the actual protocol work. 652This is the class which does the actual protocol work.
494 653
495=over 4 654=over 4
496 655
497=cut 656=cut
498 657
518our $RESOLVER; 677our $RESOLVER;
519 678
520sub resolver() { 679sub resolver() {
521 $RESOLVER || do { 680 $RESOLVER || do {
522 $RESOLVER = new AnyEvent::DNS; 681 $RESOLVER = new AnyEvent::DNS;
523 $RESOLVER->load_resolv_conf; 682 $RESOLVER->os_config;
524 $RESOLVER 683 $RESOLVER
525 } 684 }
526} 685}
527 686
528=item $resolver = new AnyEvent::DNS key => value... 687=item $resolver = new AnyEvent::DNS key => value...
533 692
534=over 4 693=over 4
535 694
536=item server => [...] 695=item server => [...]
537 696
538A list of server addressses (default C<v127.0.0.1>) in network format (4 697A list of server addressses (default: C<v127.0.0.1>) in network format (4
539octets for IPv4, 16 octets for IPv6 - not yet supported). 698octets for IPv4, 16 octets for IPv6 - not yet supported).
540 699
541=item timeout => [...] 700=item timeout => [...]
542 701
543A list of timeouts to use (also determines the number of retries). To make 702A list of timeouts to use (also determines the number of retries). To make
557 716
558Most name servers do not handle many parallel requests very well. This option 717Most name servers do not handle many parallel requests very well. This option
559limits the numbe rof outstanding requests to C<$n> (default: C<10>), that means 718limits the numbe rof outstanding requests to C<$n> (default: C<10>), that means
560if you request more than this many requests, then the additional requests will be queued 719if you request more than this many requests, then the additional requests will be queued
561until some other requests have been resolved. 720until some other requests have been resolved.
721
722=item reuse => $seconds
723
724The number of seconds (default: C<60>) that a query id cannot be re-used
725after a request. Since AnyEvent::DNS will only allocate up to 30000 ID's
726at the same time, the long-term maximum number of requests per second is
727C<30000 / $seconds> (and thus C<500> requests/s by default).
562 728
563=back 729=back
564 730
565=cut 731=cut
566 732
576 server => [v127.0.0.1], 742 server => [v127.0.0.1],
577 timeout => [2, 5, 5], 743 timeout => [2, 5, 5],
578 search => [], 744 search => [],
579 ndots => 1, 745 ndots => 1,
580 max_outstanding => 10, 746 max_outstanding => 10,
581 reuse => 300, # reuse id's after 5 minutes only, if possible 747 reuse => 60, # reuse id's after 5 minutes only, if possible
582 %arg, 748 %arg,
583 fh => $fh, 749 fh => $fh,
584 reuse_q => [], 750 reuse_q => [],
585 }, $class; 751 }, $class;
586 752
596} 762}
597 763
598=item $resolver->parse_resolv_conv ($string) 764=item $resolver->parse_resolv_conv ($string)
599 765
600Parses the given string a sif it were a F<resolv.conf> file. The following 766Parses the given string a sif it were a F<resolv.conf> file. The following
601directives are supported: 767directives are supported (but not neecssarily implemented).
602 768
603C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>, 769C<#>-style comments, C<nameserver>, C<domain>, C<search>, C<sortlist>,
604C<options> (C<timeout>, C<attempts>, C<ndots>). 770C<options> (C<timeout>, C<attempts>, C<ndots>).
605 771
606Everything else is silently ignored. 772Everything else is silently ignored.
650 if $attempts; 816 if $attempts;
651 817
652 $self->_compile; 818 $self->_compile;
653} 819}
654 820
655=item $resolver->load_resolv_conf 821=item $resolver->os_config
656 822
657Tries to load and parse F</etc/resolv.conf>. If there will ever be windows 823Tries so load and parse F</etc/resolv.conf> on portable opertaing systems. Tries various
658support, then this function will do the right thing under windows, too. 824egregious hacks on windows to force the dns servers and searchlist out of the config.
659 825
660=cut 826=cut
661 827
662sub load_resolv_conf { 828sub os_config {
663 my ($self) = @_; 829 my ($self) = @_;
664 830
831 if ($^O =~ /mswin32|cygwin/i) {
832 # yeah, it suxx... lets hope DNS is DNS in all locales
833
834 if (open my $fh, "ipconfig /all |") {
835 delete $self->{server};
836 delete $self->{search};
837
838 while (<$fh>) {
839 # first DNS.* is suffix list
840 if (/^\s*DNS/) {
841 while (/\s+([[:alnum:].\-]+)\s*$/) {
842 push @{ $self->{search} }, $1;
843 $_ = <$fh>;
844 }
845 last;
846 }
847 }
848
849 while (<$fh>) {
850 # second DNS.* is server address list
851 if (/^\s*DNS/) {
852 while (/\s+(\d+\.\d+\.\d+\.\d+)\s*$/) {
853 my $ip = $1;
854 push @{ $self->{server} }, AnyEvent::Util::socket_inet_aton $ip
855 if AnyEvent::Util::dotted_quad $ip;
856 $_ = <$fh>;
857 }
858 last;
859 }
860 }
861
862 $self->_compile;
863 }
864 } else {
865 # try resolv.conf everywhere
866
665 open my $fh, "</etc/resolv.conf" 867 if (open my $fh, "</etc/resolv.conf") {
666 or return;
667
668 local $/; 868 local $/;
669 $self->parse_resolv_conf (<$fh>); 869 $self->parse_resolv_conf (<$fh>);
870 }
871 }
670} 872}
671 873
672sub _compile { 874sub _compile {
673 my $self = shift; 875 my $self = shift;
674 876
698} 900}
699 901
700sub _recv { 902sub _recv {
701 my ($self) = @_; 903 my ($self) = @_;
702 904
703 while (my $peer = recv $self->{fh}, my $res, 4000, 0) { 905 while (my $peer = recv $self->{fh}, my $res, 4096, 0) {
704 my ($port, $host) = Socket::unpack_sockaddr_in $peer; 906 my ($port, $host) = Socket::unpack_sockaddr_in $peer;
705 907
706 return unless $port == 53 && grep $_ eq $host, @{ $self->{server} }; 908 return unless $port == 53 && grep $_ eq $host, @{ $self->{server} };
707 909
708 $self->_feed ($res); 910 $self->_feed ($res);
723 }), sub { 925 }), sub {
724 my ($res) = @_; 926 my ($res) = @_;
725 927
726 if ($res->{tc}) { 928 if ($res->{tc}) {
727 # success, but truncated, so use tcp 929 # success, but truncated, so use tcp
728 AnyEvent::Util::tcp_connect +(Socket::inet_ntoa $server), 53, sub { 930 AnyEvent::Socket::tcp_connect ((Socket::inet_ntoa $server), 53, sub {
729 my ($fh) = @_ 931 my ($fh) = @_
730 or return $self->_exec ($req, $retry + 1); 932 or return $self->_exec ($req, $retry + 1);
731 933
732 my $handle = new AnyEvent::Handle 934 my $handle = new AnyEvent::Handle
733 fh => $fh, 935 fh => $fh,
742 $self->_feed ($_[1]); 944 $self->_feed ($_[1]);
743 }); 945 });
744 }); 946 });
745 shutdown $fh, 1; 947 shutdown $fh, 1;
746 948
747 }, sub { $timeout }; 949 }, sub { $timeout });
748 950
749 } else { 951 } else {
750 # success 952 # success
751 $self->{id}{$req->[2]} = 1; 953 $self->{id}{$req->[2]} = 1;
752 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $req->[2]]; 954 push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $req->[2]];
774 976
775 $NOW = time; 977 $NOW = time;
776 978
777 # first clear id reuse queue 979 # first clear id reuse queue
778 delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] } 980 delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] }
779 while @{ $self->{reuse_q} } && $self->{reuse_q}[0] <= $NOW; 981 while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW;
780 982
781 while ($self->{outstanding} < $self->{max_outstanding}) { 983 while ($self->{outstanding} < $self->{max_outstanding}) {
984
985 if (@{ $self->{reuse_q} } >= 30000) {
986 # we ran out of ID's, wait a bit
987 $self->{reuse_to} ||= AnyEvent->timer (after => $self->{reuse_q}[0][0] - $NOW, cb => sub {
988 delete $self->{reuse_to};
989 $self->_scheduler;
990 });
991 last;
992 }
993
782 my $req = shift @{ $self->{queue} } 994 my $req = shift @{ $self->{queue} }
783 or last; 995 or last;
784 996
785 while () { 997 while () {
786 $req->[2] = int rand 65536; 998 $req->[2] = int rand 65536;
958 }; 1170 };
959 1171
960 $do_search->(); 1172 $do_search->();
961} 1173}
962 1174
1175use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end
1176
9631; 11771;
964 1178
965=back 1179=back
966 1180
967=head1 AUTHOR 1181=head1 AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines