+++ Net/DNS/Resolver.pm Wed Jul 30 09:51:04 2008 @@ -729,8 +729,6 @@ The current implementation supports TSIG only on outgoing packets. No validation of server replies is performed. -bgsend does not honor the usevc flag and only uses UDP for transport. - =head1 COPYRIGHT Copyright (c) 1997-2002 Michael Fuhr. ============================================================================== +++ Net/DNS/Resolver/Base.pm @@ -921,6 +921,10 @@ my $packet = $self->make_query_packet(@_); my $packet_data = $packet->data; + if ($self->{'usevc'}) { + return $self->bgsend_tcp($packet, $packet_data); + } + my $srcaddr = $self->{'srcaddr'}; my $srcport = $self->{'srcport'}; @@ -1010,38 +1014,127 @@ } +sub bgsend_tcp { + my ($self, $packet, $packet_data) = @_; + my $lastanswer; + + my $srcport = $self->{'srcport'}; + my $srcaddr = $self->{'srcaddr'}; + my $dstport = $self->{'port'}; + + unless ( $self->nameservers()) { + $self->errorstring('no nameservers'); + print ";; ERROR: bgsend_tcp: no nameservers\n" if $self->{'debug'}; + return; + } + + $self->_reset_errorstring; + + foreach my $ns ($self->nameservers()) { + print ";; attempt to bgsend_tcp($ns:$dstport) (src port = $srcport)\n" + if $self->{'debug'}; + + my $sock; + my $sock_key = "$ns:$dstport"; + my ($host,$port); + if ($self->persistent_tcp && $self->{'sockets'}[AF_UNSPEC]{$sock_key}) { + $sock = $self->{'sockets'}[AF_UNSPEC]{$sock_key}; + print ";; using persistent socket\n" + if $self->{'debug'}; + unless ($sock->connected){ + print ";; persistent socket disconnected (trying to reconnect)" + if $self->{'debug'}; + undef($sock); + $sock = $self->_create_tcp_socket($ns); + next unless $sock; + $self->{'sockets'}[AF_UNSPEC]{$sock_key} = $sock; + } + } else { + $sock = $self->_create_tcp_socket($ns); + next unless $sock; + + $self->{'sockets'}[AF_UNSPEC]{$sock_key} = $sock if $self->persistent_tcp; + } + + my $lenmsg = pack('n', length($packet_data)); + print ';; sending ', length($packet_data), " bytes\n" + if $self->{'debug'}; + + # note that we send the length and packet data in a single call + # as this produces a single TCP packet rather than two. This + # is more efficient and also makes things much nicer for sniffers. + # (ethereal doesn't seem to reassemble DNS over TCP correctly) + + unless ($sock->send( $lenmsg . $packet_data)) { + $self->errorstring($!); + print ";; ERROR: send_tcp: data send failed: $!\n" + if $self->{'debug'}; + next; + } + + return $sock; + } + return; +} + sub bgread { my ($self, $sock) = @_; my $buf = ''; - my $peeraddr = $sock->recv($buf, $self->_packetsz); - - if ($peeraddr) { + my $peeraddr; + unless ($self->{'usevc'}) { + $peeraddr = $sock->recv($buf, $self->_packetsz); + } + + if ($self->{'usevc'}) { + $buf = read_tcp($sock, Net::DNS::INT16SZ(), $self->{'debug'}); + my ($len) = unpack('n', $buf); + print ";; can't determine length: $buf\n" unless $len; + my $sel = IO::Select->new($sock); + unless ($sel->can_read($self->{'tcp_timeout'})) { + $self->errorstring('timeout'); + print ";; TIMEOUT\n" if $self->{'debug'}; + next; + } + $buf = read_tcp($sock, $len, $self->{'debug'}); + $self->answerfrom($sock->peerhost); + + if ($buf) { + print ';; answer from ', $sock->peerhost, ':', + $sock->peerport, ' : ', length($buf), " bytes\n" + if $self->{'debug'}; + } + } elsif ($peeraddr) { print ';; answer from ', $sock->peerhost, ':', $sock->peerport, ' : ', length($buf), " bytes\n" if $self->{'debug'}; - - my ($ans, $err) = Net::DNS::Packet->new(\$buf, $self->{'debug'}); - - if (defined $ans) { - $self->errorstring($ans->header->rcode); - $ans->answerfrom($sock->peerhost); - } elsif (defined $err) { - $self->errorstring($err); - } - - return $ans; } else { $self->errorstring($!); return; } + + my ($ans, $err) = Net::DNS::Packet->new(\$buf, $self->{'debug'}); + + if (defined $ans) { + $self->errorstring($ans->header->rcode); + $ans->answerfrom($sock->peerhost); + } elsif (defined $err) { + $self->errorstring($err); + } + + return $ans; } sub bgisready { my $self = shift; my $sel = IO::Select->new(@_); - my @ready = $sel->can_read(0.0); + my @ready; + if ($self->{'usevc'}) { + @ready = $sel->can_read($self->{'tcp_timeout'}); + } else { + @ready = $sel->can_read(0.0); + } return @ready > 0; }