File Coverage

blib/lib/IO/Socket/DNS/Server.pm
Criterion Covered Total %
statement 24 337 7.1
branch 0 158 0.0
condition 0 57 0.0
subroutine 8 27 29.6
pod 2 15 13.3
total 34 594 5.7


line stmt bran cond sub pod time code
1             package IO::Socket::DNS::Server;
2              
3 1     1   24421 use strict;
  1         2  
  1         37  
4 1     1   5 use warnings;
  1         2  
  1         27  
5 1     1   5 use Carp qw(croak);
  1         2  
  1         79  
6 1     1   872 use IO::Socket;
  1         28446  
  1         5  
7 1     1   7838 use IO::Select;
  1         1779  
  1         51  
8 1     1   713 use IO::Socket::DNS;
  1         3  
  1         33  
9 1     1   7 use base qw(Net::DNS::Nameserver);
  1         2  
  1         991  
10 1     1   90631 use Data::Dumper; # Only for debugging
  1         12493  
  1         5983  
11              
12             our $VERSION = '0.021';
13              
14             # Maximum number of bytes to try to encode into the response packet
15             our $MAX_RETURN = 100;
16              
17             # Probe "z" timeout for TCP socket reading (in seconds)
18             our $PROBE_TIMEOUT = 0.1;
19              
20             # No semi-colon allowed in TXT value
21             # No non-printing characters allowed
22             # No newlines allowed
23             # No backslash allowed
24             # No double quotes allowed because it will be enclosed later
25             our $TXT = {
26             "" => q{dig +short TXT loader.$suffix},
27             netdns => q{{$b=chr(34)}while(++$a&&`nslookup -type=TXT netdns$a.$suffix. 2>&1`=~/$b([0-9a-f]+)$b/){$_.=$1} eval pack'H*',$_ or warn $@},
28             netdns0 => "netdns_code",
29             loader => q{echo eval pack q/N6/,0x60404152,0x4756603d,0x7e2f2228,0x2e2a2922,0x2f3b6576,0x616c2431 | perl - nslookup -type=TXT unzip.$suffix.},
30             unzip => q{{$b=chr(34)}while(++$a&&`nslookup -type=TXT unzip$a.$suffix. 2>&1`=~/$b([0-9a-f]+)$b/){$_.=$1} eval pack'H*',$_ or warn qq{$_:$@}},
31             unzip0 => "unzip_code",
32             menu => q{while(++$a and $b=eval{[Net::DNS::Resolver->new->query(qq(menu$a.$suffix),'TXT')->answer]->[0]->txtdata}){$_.=$b}eval pack'H*',$_ or warn$@},
33             menu0 => "menu_code",
34             client0 => "client_code",
35             dnsc0 => "dnsc_code",
36             dnsssh0 => "dnsssh_code",
37             dnsnc0 => "dnsnc_code",
38             # No double nor single quotes nor dollar sign nor backticks nor any shell metas allowed for this
39             # special "nslookup" value in order to function under different environments and OSes and shells,
40             # including Linux, Mac, Win32, Cygwin, Windows, DOS, CMD.EXE, bash, tcsh, csh, ksh, zsh, etc.
41             nslookup=> q{echo eval pack q/N6/,0x60404152,0x4756603d,0x7e2f2228,0x2e2a2922,0x2f3b6576,0x616c2431 | perl - nslookup -type=TXT netdns.$suffix.},
42             };
43              
44             # new
45             sub new {
46 0     0 1   my $class = shift;
47              
48 0           my %args = @_;
49 0           my $reply_handler = $args{ReplyHandler};
50 0     0     $args{ReplyHandler} = sub { return "SERVFAIL", [], [], [] }; # Avoid: "No reply handler!";
  0            
51 0 0 0       $args{Suffix} ||= $ENV{DNS_SUFFIX}
52             or croak "Suffix is required";
53 0           my $suffix = $args{Suffix} = lc $args{Suffix};
54 0   0       $args{"Verbose"} ||= 0;
55 0   0       $args{"Password"} = $ENV{DNS_PASSWORD} || $args{Password} || "";
56 0   0       $args{"SOA"} ||= do {
57 0   0       my $res = $args{net_dns} ||= eval {
58 0           require Net::DNS::Resolver::Recurse;
59 0           return Net::DNS::Resolver::Recurse->new;
60             };
61 0           my $soa = { lc($suffix) => 1 };
62 0           my $ip = undef;
63 0           my $bind_errors = {};
64             $res->recursion_callback(sub {
65 0     0     my $packet = shift;
66 0           foreach my $rr ($packet->answer,$packet->authority,$packet->additional) {
67 0 0 0       if ($rr->type eq "NS" && $soa->{lc $rr->name}) {
68 0           $soa->{lc $rr->nsdname} = 1;
69             }
70             }
71 0           foreach my $rr ($packet->answer,$packet->authority,$packet->additional) {
72 0 0 0       if ($rr->type eq "CNAME" && $soa->{lc $rr->name}) {
73 0           $soa->{lc $rr->nsdname} = 1;
74             }
75             }
76 0           foreach my $rr ($packet->answer,$packet->authority,$packet->additional) {
77 0 0 0       if ($rr->type eq "A" && $soa->{lc $rr->name}) {
78 0           my $try = $rr->rdatastr;
79 0 0         if (!$bind_errors->{$try}) {
80 0 0         warn "Testing $try ...\n" if $args{"Verbose"};
81             # Quick Ephermural Test to make sure this address is bindable.
82 0 0         if (IO::Socket::INET->new(LocalAddr => $try, Listen => 1)) {
83 0           $ip = $rr->rdatastr;
84 0 0         warn "Automatically determined DNS suffix [$suffix] to have SOA IP [$ip]\n" if $args{"Verbose"};
85 0           die "found winner $ip";
86             }
87             else {
88 0           $bind_errors->{$try} = $!;
89 0 0         warn "Unable to bind to $try: $!\n" if $args{"Verbose"};
90             }
91             }
92             }
93             }
94 0           });
95              
96 0           my $num_soas = 0;
97 0           while ($num_soas < scalar(keys %$soa)) {
98 0           $num_soas = scalar keys %$soa;
99 0           foreach my $auth (sort keys %$soa) {
100 0           eval { $res->query_dorecursion($auth, "ANY") };
  0            
101 0 0         last if $ip;
102             }
103 0 0         last if $ip;
104             }
105              
106 0 0         if (!$ip) {
107 0           ($ip) = keys %$bind_errors;
108 0 0         if ($ip) {
109 0           warn "Warning: Unable to bind to $ip but using it for the SOA IP anyway. Specify SOA manually if you don't like this.\n";
110             }
111             else {
112 0           die "Unable to determine SOA IP using Suffix [$suffix]. Please correct the DNS authority entries or try another Suffix.\n";
113             }
114             }
115              
116 0           $ip;
117             };
118 0   0       $args{"LocalAddr"} ||= $args{"SOA"};
119              
120 0           my $self = $class->SUPER::new(%args);
121             # Now swap in the real handler
122 0   0 0     $self->{"ReplyHandler"} = $reply_handler || sub { ReplyHandler($self, @_); };
  0            
123              
124 0 0         warn "DEBUG: Launching with suffix [$args{Suffix}]\n" if $args{"Verbose"};
125 0           return $self;
126             }
127              
128             sub ReplyHandler {
129 0     0 0   my $self = shift;
130 0 0         my $suffix = $self->{"Suffix"} or croak "ReplyHandler: called incorrectly! Missing Suffix?";
131 0           my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
132 0           my ($rcode, @ans, @auth, @add, $aa);
133              
134 0           $qname =~ y/A-Z/a-z/;
135 0 0         warn "DEBUG: Q: $qname $qtype (from $peerhost)...\n" if $self->{"Verbose"};
136 0 0         if ($qname =~ /(^|\.)$suffix/) {
137 0           $aa = 1;
138 0 0         if ($qtype eq "TXT") {
    0          
    0          
139 0           my $ans = "";
140 0 0 0       if ($qname =~ /^([a-z]*)\.?$suffix/ and
    0 0        
    0 0        
    0 0        
141             my $static = $TXT->{$1}) {
142 0           $ans = qq{"$static"};
143 0           $ans =~ s/\$suffix/$suffix/g;
144             }
145             elsif ($qname =~ /^([a-z\-]+)(\d+)\.$suffix$/ and
146             my $method = $TXT->{$1."0"}) {
147 0           my $prefix = $1;
148 0           my $line_num = $2;
149 0   0       my $codes_array_ref = $self->{"_code_array_cache_$prefix"} ||= eval {
      0        
150             my $code = ref($method) eq "CODE" ? $method->($self,$prefix) : $self->$method($prefix);
151             warn "DEBUG: $method string=[$code]\n" if $self->{"Verbose"};
152             my @encode = ();
153             while ($code =~ s/^(.{1,100})//s) {
154             my $chunk = $1;
155             push @encode, unpack "H*", $chunk;
156             }
157             warn Dumper [ code_array => \@encode ] if $self->{"Verbose"};
158             return \@encode;
159             } || [];
160              
161 0 0         $ans = $line_num ? $codes_array_ref->[$line_num - 1] : scalar @$codes_array_ref if $line_num <= @$codes_array_ref;
    0          
162             }
163             # Check for TCP SYN Request
164             elsif ($qname =~ /^([a-z0-9\-\.]+)\.t(\d+)\.(\w+)\.(0|z[0-9a-f]{26})\.$suffix$/i) {
165 0           my $peerhost = $1;
166 0           my $peerport = $2;
167 0           my $ephid = $3;
168 0           my $code = $4;
169 0 0         if ($code ne $self->encrypt($peerhost, $peerport)) {
    0          
170 0 0         $IO::Socket::DNS::INVALID_PASS or die "Implementation fail: Sentinal value missing?";
171 0           $ans = "$ephid.$IO::Socket::DNS::INVALID_PASS";
172             }
173             elsif (my $prev = $self->{"_proxy"}->{$ephid}) {
174 0           $ans = "$ephid.0.$prev->{next_seqid}";
175 0 0         if (my $sent = $prev->{"sent"}) {
176 0           my $banner = $self->dnsencode($sent);
177 0           $banner =~ s/\.//g;
178             # Recreate original response exactly as before
179 0           $ans .= ".".length($banner).".$banner";
180             }
181             }
182             else {
183 0 0         warn "Sending TCP SYN to $peerhost:$peerport\n" if $self->{"Verbose"};
184 0           my $sock = new IO::Socket::INET
185             PeerAddr => $peerhost,
186             PeerPort => $peerport,
187             Timeout => 30,
188             ;
189 0 0 0       my $errno = $sock ? 0 : ($! + 0) || -1;
190 0           $ans = "$ephid.$errno";
191 0 0         if (!$sock) {
192 0 0         warn "Failed to connect to $peerhost:$peerport (errno=$errno)\n" if $self->{"Verbose"};
193             }
194             else {
195 0           my $seqid = $self->gen_seqid;
196 0           $ans .= ".$seqid";
197 0 0         warn "Received ACK for $peerhost:$peerport (seqid=$seqid)\n" if $self->{"Verbose"};
198             # Disable blocking. Buffer data to ensure it all gets sent eventually.
199 0           $sock->blocking(0);
200 0           my $timeout = time()+120;
201 0           $self->{"_tcp"}->{$sock} = {
202             ephid => $ephid,
203             seqid => $seqid,
204             peer => "tcp:$peerhost:$peerport",
205             state => -1,
206             socket => $sock,
207             timeout=> $timeout,
208             inbuffer => "",
209             };
210 0           $self->{"_proxy"}->{$ephid} = {
211             socket => $sock,
212             inbuffer => "",
213             sent => "",
214             timeout => $timeout,
215             next_seqid => $seqid,
216             };
217 0           $self->{"_proxy"}->{$seqid} = {
218             socket => $sock,
219             inbuffer => "",
220             sent => undef,
221             timeout => $timeout,
222             ephid => $ephid,
223             next_seqid => undef,
224             };
225             # Brief wait for a possible protocol banner
226 0 0         if (IO::Select->new($sock)->can_read(0.3)) {
227             # Found response. Grab what is available.
228 0           my $banner;
229 0 0         if (sysread($sock, $banner, $MAX_RETURN)) {
230 0           $self->{"_proxy"}->{$ephid}->{"sent"} = $banner;
231 0           $banner = $self->dnsencode($banner);
232 0           $banner =~ s/\.//g;
233             # Add content to the answer
234 0           $ans .= ".".length($banner).".$banner";
235             }
236             }
237 0           $self->{"select"}->add($sock);
238             }
239             }
240             #warn Dumper DEBUG => [ full_tcp => $self->{_tcp}, _proxy => $self->{_proxy}, ] if $self->{"Verbose"};
241             }
242             # Check for SEND
243             elsif (($qname =~ /^([0-9a-w]{6})\.(\d+)\.([0-9a-w.]+)\.$suffix$/ && $2 == length($3)) ||
244             $qname =~ /^([0-9a-w]{6})\.()([xz])\.$suffix$/ and
245             my $proxy = $self->{"_proxy"}->{$1}) {
246 0           my $seqid = $1;
247 0           my $encoded = $3;
248 0           my $sock = $proxy->{"socket"};
249 0 0         if ($encoded =~ /^[xz]$/) {
250 0 0 0       if ($encoded eq "x" and my $tcp = $self->{"_tcp"}->{$sock}) {
251             # Client wants to shutdown the connection
252             #shutdown($sock,1);
253             # Expire the connection immediately
254 0           $tcp->{"timeout"} = time() - 1;
255 0           $self->loop_once(0);
256             }
257 0           $encoded = "";
258             }
259 0           $ans = "$seqid-";
260 0           my $next_seqid = $proxy->{"next_seqid"};
261 0 0         if ($next_seqid) {
262 0 0         warn "DEBUG: ALREADY SENT TO [$seqid] PACKET [$encoded] (skipping this time)\n" if $self->{"Verbose"};
263 0           $ans .= "$next_seqid.";
264 0           my $sent = $proxy->{"sent"};
265 0 0         if (!defined $sent) {
    0          
266 0           $ans = "$seqid.0";
267             }
268             elsif (my $len = length $sent) {
269 0           $ans .= "$len.$sent";
270             }
271             else {
272 0           $ans .= "0";
273             }
274 0 0         warn "DEBUG: Repeating cached response [$ans]\n" if $self->{"Verbose"};
275             }
276             else {
277 0 0         warn "DEBUG: SENDING TO [$seqid] PACKET [$encoded]\n" if $self->{"Verbose"};
278 0 0         if (length $encoded) {
279 0           my $decoded = $self->dnsdecode($encoded);
280 0 0         $self->{"_tcp"}->{$sock}->{"outbuffer"} .= $decoded if $self->{"_tcp"}->{$sock};
281 0           $decoded =~ s/%/%25/g;
282 0           $decoded =~ s/([^\ -\~])/sprintf "%%%02X", ord $1/eg;
  0            
283 0 0         warn "DEBUG: JAMMED INTO SOCKET [$decoded]\n" if $self->{"Verbose"};
284             }
285 0           $self->loop_once($PROBE_TIMEOUT);
286             # Consume as much inbuffer as possible
287             # and save the rest for the next seqid.
288 0           my $buffer = $proxy->{"inbuffer"};
289 0           $proxy->{"inbuffer"} = "";
290 0           my $send = "";
291 0           my $len = length $buffer;
292 0 0 0       if (!$len && !$self->{"_tcp"}->{$sock}) {
293             # Socket has been shutdown and buffer is empty
294 0           $proxy->{"sent"} = undef;
295 0           $proxy->{"next_seqid"} = -1;
296 0           $ans = "$seqid.0";
297             }
298             else {
299 0 0         if ($len) {
300 0 0         my $consume = $len >= $MAX_RETURN ? $MAX_RETURN : $len;
301 0           $send = substr($buffer, 0, $consume, "");
302             }
303 0 0         if (defined (my $consumed = $send)) {
304 0           $consumed =~ s/%/%25/g;
305 0           $consumed =~ s/([^\ -\~])/sprintf "%%%02X", ord $1/eg;
  0            
306 0 0         warn "DEBUG: EXTRACTED FROM SOCKET [$consumed]\n" if $self->{"Verbose"};
307             }
308              
309 0           $send = $self->dnsencode($send);
310 0           $len = length($send);
311 0           $proxy->{"sent"} = $send;
312              
313             # Generate next seqid
314 0           $next_seqid = $self->gen_seqid;
315 0           $proxy->{"next_seqid"} = $next_seqid;
316 0           $ans .= "$next_seqid.$len";
317 0 0         $ans .= ".$send" if $len;
318 0           $self->{"_proxy"}->{$next_seqid} = {
319             socket => $sock,
320             inbuffer => $buffer,
321             sent => undef,
322             timeout => time()+120,
323             ephid => $proxy->{"ephid"},
324             next_seqid => undef,
325             };
326             # Update the seqid to point to the new one.
327 0 0         $self->{"_tcp"}->{$sock}->{"seqid"} = $next_seqid if $self->{"_tcp"}->{$sock};
328             }
329             }
330             }
331 0 0         if ($ans) {
332 0 0         warn "DEBUG: $qname RESPONSE [$ans]\n" if $self->{"Verbose"};
333 0           push @ans, Net::DNS::RR->new(qq{$qname 60 $qclass $qtype $ans});
334 0           $rcode = "NOERROR";
335             }
336             }
337             elsif ($qtype eq "NS") {
338 0           my $me = $self->{SOA};
339 0           push @ans, Net::DNS::RR->new("$qname 60 $qclass $qtype dns.$suffix");
340 0           push @auth, Net::DNS::RR->new("$qname 60 $qclass $qtype dns.$suffix");
341 0           push @add, Net::DNS::RR->new("dns.$suffix 60 $qclass A $me");
342 0           $rcode = "NOERROR";
343             }
344             elsif ($qtype =~ /^(A|CNAME)$/) {
345 0           my $me = $self->{SOA};
346 0           my $alias = "please-use-TXT-instead-of-$qtype-when-looking-up.loader.$suffix";
347 0 0         if ($qname =~ /^(dns\.|)\Q$suffix\E$/) {
    0          
348 0           push @ans, Net::DNS::RR->new("$qname 60 $qclass A $me");
349             }
350             elsif ($qname eq $suffix) {
351             # It violates RFC to CNAME to subdomain of itself.
352 0           push @ans, Net::DNS::RR->new("$qname 1 $qclass CNAME $alias");
353 0           push @ans, Net::DNS::RR->new("$alias 1 $qclass A $me");
354 0           push @add, Net::DNS::RR->new("dns.$suffix 60 $qclass A $me");
355             }
356             else {
357 0           push @ans, Net::DNS::RR->new("$qname 10 $qclass CNAME $alias");
358 0           push @ans, Net::DNS::RR->new("$alias 10 $qclass CNAME dns.$suffix");
359 0           push @add, Net::DNS::RR->new("dns.$suffix 60 $qclass A $me");
360             }
361 0           push @auth, Net::DNS::RR->new("$suffix 60 $qclass NS dns.$suffix");
362 0           $rcode = "NOERROR";
363             }
364             }
365             else {
366 0           push @auth, Net::DNS::RR->new(". 86400 IN NS a.root-servers.net");
367 0           $rcode = "NOERROR";
368             }
369              
370 0   0       $rcode ||= "NXDOMAIN";
371              
372 0           return ($rcode, \@ans, \@auth, \@add, { aa => $aa });
373             }
374              
375             sub gen_seqid {
376 0     0 0   my $seqid = "";
377 0           for (1..6) {
378 0           $seqid .= $IO::Socket::DNS::a32->[rand @$IO::Socket::DNS::a32];
379             }
380 0           return $seqid;
381             }
382              
383             sub netdns_code {
384 0     0 0   my $self = shift;
385 0           my $suffix = $self->{"Suffix"};
386 0           my $LOADER = $TXT->{"loader"};
387 0           $LOADER =~ s/"(.+)"/$1/;
388 0           my @modules = ();
389             my $net_dns_handler = sub {
390 0     0     my $self = shift;
391 0 0         my $me = shift or die "netdns module is required";
392 0 0         my $full_path = $self->{"_netdns_map"}->{$me} or die "$me: Installed handler, but no map?";
393 0 0         warn "DEBUG: Loading [$full_path] ...\n" if $self->{"Verbose"};
394 0 0         open my $fh, "<", $full_path or die "$full_path: Found in \%INC but unable to read?";
395 0           my $code = "";
396 0           while (<$fh>) {
397 0 0         last if /^__END__/;
398 0           $code .= $_;
399             }
400 0           close $fh;
401 0           return $code;
402 0           };
403             # This is just a hack to allow a Non-Win32 server to still
404             # download Win32.pm in case it is needed by a Win32 client:
405 0           eval { require Net::DNS::Resolver::Win32 };
  0            
406 0           foreach my $mod (sort keys %INC) {
407 0 0         if ($mod =~ m{^Net/DNS}) {
408 0           push @modules, $mod;
409 0           my $p = lc $mod;
410 0           $p =~ s/\.pm//;
411 0           $p =~ s{/+}{-}g;
412 0           $p =~ y/0-9//d;
413 0           my $full_path = $INC{$mod};
414 0           my $method = $p."0";
415 0 0         print "INSTALLING HANDLER: $p\$n.$suffix => $full_path\n" if $self->{"Verbose"};
416 0           $self->{"_netdns_map"}->{$p} = $full_path;
417 0           $TXT->{$method} = $net_dns_handler;
418             }
419             }
420 0           my $MODULES = "@modules";
421              
422             # Short Program to bootstrap Net::DNS onto the client
423 0           my $code = q{
424             use strict;
425             # Hot flush STDOUT
426             $| = 1;
427             unshift @INC, "lib";
428             # Stub program for testing purposes just for now.
429             print "Loading Net::DNS::* modules through nslookup via netdns.$suffix ...\n";
430             my @modules = qw($MODULES);
431             my $downloaded = 0;
432             foreach my $mod (@modules) {
433             print "Testing: $mod ...\n";
434             my $pre = lc $mod;
435             $pre =~ s/\.pm//;
436             $pre =~ s{/+}{-}g;
437             $pre =~ y/0-9//d;
438             my $file = "lib/$mod";
439             if (eval "require '$mod'") {
440             # Module loaded fine
441             }
442             elsif (-s $file) {
443             # File already exists
444             print "$file: File exists so refusing to download again.\n";
445             }
446             else {
447             warn "FAILED: $@";
448             my $dir = "";
449             while ($file =~ m{([^/]+)/}g) {
450             $dir .= $1;
451             mkdir $dir, 0755;
452             $dir .= "/";
453             }
454             my $i = 0;
455             my $contents = "";
456             print "Downloading $file ...\n";
457             $downloaded++;
458             my $ticks = 0;
459             while (1) {
460             `nslookup -type=TXT $pre$i.$suffix 2>&1` =~ /"(.+)"/ or
461             warn("**CHOKE1** $pre$i\n") && sleep(1) && `nslookup -type=TXT $pre$i.$suffix 2>&1` =~ /"(.+)"/ or
462             warn("**CHOKE2** $pre$i\n") && sleep(1) && `nslookup -type=TXT $pre$i.$suffix 2>&1` =~ /"(.+)"/;
463             my $txt = $1 or last;
464             if ($i) {
465             $contents .= $txt;
466             print sprintf "\r(%d/%d) %.1f%%", $i, $ticks, $i/$ticks*100;
467             }
468             elsif ($txt =~ /^\d+$/) {
469             $ticks = $txt;
470             print "\r0/$ticks";
471             }
472             else {
473             die "$pre$i: Invalid DNS cache: $txt\n";
474             }
475             $i++;
476             last if $i > $ticks;
477             }
478             print "\n";
479             if ($i<$ticks) {
480             print "WARNING! Only downloaded $i/$ticks chunks do refusing to write $file\n";
481             next;
482             }
483             $contents = pack 'H*', $contents;
484             if ($contents) {
485             open my $fh, ">", $file or die "$file: open: $!";
486             print $fh $contents;
487             close $fh;
488             }
489             }
490             }
491              
492             if ($downloaded) {
493             foreach my $mod (@modules) {
494             next if $mod =~ /Win32/ and $^O !~ /Win32/;
495             eval "require '$mod'" or die "$mod: Unable to download?: $@";
496             }
497             }
498             else {
499             warn "Congratulations! You already had Net::DNS installed.\n";
500             }
501             my $n = q{$LOADER};
502             $n =~ s/\bperl\b/$^X/g;
503             print "Now you are safe to run the following:\n\n$n\n\n";
504             exit;
505             };
506              
507             # Strip comments
508 0           $code =~ s/\s+\#.*//g;
509             # Fake interpolate $LOADER
510 0           $code =~ s/\$LOADER/$LOADER/g;
511             # Fake inerpolate $MODULES
512 0           $code =~ s/\$MODULES/$MODULES/g;
513             # Fake interpolate $suffix
514 0           $code =~ s/\$suffix/$suffix/g;
515             # Jam true VERSION
516 0           $code =~ s/\$VERSION/$IO::Socket::DNS::VERSION/g;
517             # Collapse to reduce transport code
518 0           $code =~ s/\s+/ /g;
519 0           return $code;
520             }
521              
522             sub unzip_code {
523 0     0 0   my $self = shift;
524 0           my $suffix = $self->{"Suffix"};
525              
526             # Short program to CREATE the menu.pl program.
527 0           my $code = q{
528             $| = 1;
529             use strict;
530             use warnings;
531              
532             my $interp = $^X;
533             if ($interp !~ m{[\\/]}) {
534             # Make fully qualified absolute search path
535             foreach my $path (split m/:/, $ENV{PATH}) {
536             my $try = "$path/$interp";
537             if (-e $try) {
538             $interp = $try;
539             last;
540             }
541             }
542             }
543              
544             if (-e "menu.pl") {
545             print "File menu.pl already exists. You must remove it to regenerate a fresh copy.\n";
546             }
547             else {
548             print "Creating menu.pl ...\n";
549             open my $fh, ">", "menu.pl" or die "menu.pl: open: $!\n";
550             print $fh qq{\#!$interp -w\n};
551             print $fh q{
552             use strict;
553             print "Loading MENU. Please wait...\n";
554             my $res = eval {
555             require Net::DNS::Resolver;
556             Net::DNS::Resolver->new;
557             };
558             my $get_txt = $res ? sub {
559             my $q = shift;
560             # Fast method, but Net::DNS may not be installed.
561             return eval{[$res->query($q,'TXT')->answer]->[0]->txtdata};
562             } : sub {
563             my $q = shift;
564             # Slower, but better than relying on Net::DNS to be installed.
565             return $1 if `nslookup -type=TXT $q. 2>&1`=~/"(.+)"/;
566             sleep 1;
567             return $1 if `nslookup -type=TXT $q. 2>&1`=~/"(.+)"/;
568             return undef;
569             };
570             $_="";
571             my $i=0;
572             while (++$i and my $b=$get_txt->("menu$i.$suffix")) {$_.=$b}
573             $_=pack 'H*', $_;
574             if (open my $fh, "+<", $0) {
575             # Self modifying code to spead up future executions.
576             print $fh "#!$^X -w\n";
577             print $fh $_;
578             close $fh;
579             exit if 0 == system $0;
580             }
581             eval or warn "$_:$@";
582             };
583             close $fh;
584             }
585             chmod 0755, "menu.pl";
586             print "You can now run: ".($^O=~/Win32/i?"$interp -w ":"./")."menu.pl\n\n";
587             exit;
588             };
589             # Strip comments
590 0           $code =~ s/\s+\#.*//g;
591             # Fake interpolate $suffix
592 0           $code =~ s/\$suffix/$suffix/g;
593             # Collapse to reduce transport code
594 0           $code =~ s/\s+/ /g;
595 0           return $code;
596             }
597              
598             sub menu_code {
599 0     0 0   my $self = shift;
600 0           my $suffix = $self->{"Suffix"};
601              
602             # Short Menu Program
603 0           my $code = q{
604             use strict;
605             $| = 1;
606             print qq{MENU:\n0. Just print version and exit.\n1. Download IO::Socket::DNS module.\n2. Download dnsc proxy client software.\n3. Download dnsnetcat client software.\n4. Download dnsssh client software.\n5. Run ssh tunneled through dns now.\n6. Install Net::DNS (optional for better performance)\nPlease make your selection: [0] };
607             use strict;
608             use warnings;
609              
610             my $choice = ;
611             $choice =~ s/\s+$// if defined $choice;
612             print "\n\n";
613             if (!$choice or $choice < 1 or $choice > 6) {
614             print "IO::Socket::DNS VERSION $VERSION\n";
615             exit;
616             }
617              
618             my $files = [
619             # Query File Mode
620             [ client => "lib/IO/Socket/DNS.pm" => 0644 ],
621             [ dnsc => "dnsc.pl" => 0755 ],
622             [ dnsnc => "dnsnetcat.pl" => 0755 ],
623             [ dnsssh => "dnsssh.pl" => 0755 ],
624             ];
625              
626             use FindBin qw($Bin);
627             if ($Bin) {
628             chdir $Bin;
629             unshift @INC, "$Bin/lib";
630             }
631             else {
632             unshift @INC, "lib";
633             }
634              
635             my $res = eval {
636             require Net::DNS::Resolver;
637             Net::DNS::Resolver->new;
638             };
639             my $get_txt = $res ? sub {
640             my $q = shift;
641             # Fast method, but Net::DNS may not be installed.
642             return eval{[$res->query($q,'TXT')->answer]->[0]->txtdata};
643             } : sub {
644             my $q = shift;
645             # Slower, but better than relying on Net::DNS
646             return $1 if `nslookup -type=TXT $q. 2>&1`=~/"(.+)"/;
647             warn "**CHOKE1** $q\n";
648             sleep 1;
649             return $1 if `nslookup -type=TXT $q. 2>&1`=~/"(.+)"/;
650             warn "**CHOKE2** $q\n";
651             sleep 1;
652             return $1 if `nslookup -type=TXT $q. 2>&1`=~/"(.+)"/;
653             warn "**CHOKE3** $q\n";
654             return undef;
655             };
656             my $install = sub {
657             my ($pre,$file,$mode) = @_;
658             my $dir = "";
659             while ($file =~ m{([^/]+)/}g) {
660             $dir .= $1;
661             mkdir $dir, 0755;
662             $dir .= "/";
663             }
664             my $i = 0;
665             my $contents = "";
666             print "Downloading $file ...\n";
667             my $ticks = 0;
668             while (my $txt = $get_txt->("$pre$i.$suffix")) {
669             if ($i) {
670             $contents .= $txt;
671             print sprintf "\r(%d/%d) %.1f%%", $i, $ticks, $i/$ticks*100;
672             }
673             elsif ($txt =~ /^\d+$/) {
674             $ticks = $txt;
675             print "\r0/$ticks";
676             }
677             else {
678             die "$pre$i: Invalid DNS cache: $txt\n";
679             }
680             $i++;
681             last if $i > $ticks;
682             }
683             print "\n";
684             $contents = pack 'H*', $contents;
685             if ($contents) {
686             open my $fh, ">", $file;
687             if ($file =~ /\.pl$/) {
688             my $interp = $^X;
689             if ($interp !~ m{[\\/]}) {
690             # Make fully qualified absolute search path
691             foreach my $path (split m/:/, $ENV{PATH}) {
692             my $try = "$path/$interp";
693             if (-e $try) {
694             $interp = $try;
695             last;
696             }
697             }
698             }
699             unless ($contents =~ s{^\#\!/\S+}{\#\!$interp}) {
700             print $fh "#!$interp\n";
701             }
702             }
703             print $fh $contents;
704             }
705             chmod $mode, $file;
706             return 1;
707             };
708              
709             if ($choice == 6) {
710             if (eval {
711             require Net::DNS;
712             require Net::DNS::Resolver;
713             }) {
714             warn "Congratulations! Net::DNS already works for you: $INC{'Net/DNS.pm'}\n";
715             }
716             else {
717             my @PREREQ_PM = qw(
718             IO::Socket
719             );
720             if ($^O eq "MSWin32") {
721             push @PREREQ_PM, qw(
722             Win32::Registry
723             Win32::IPHelper
724             );
725             }
726             my %broken = ();
727             foreach my $module (@PREREQ_PM) {
728             if (!eval "require $module") {
729             $broken{$module} = "$@";
730             }
731             }
732             if (scalar keys %broken) {
733             foreach my $broken (sort keys %broken) {
734             warn "Unable to install Net::DNS without Prerequisite Module $broken: $broken{$broken}\n";
735             }
736             exit;
737             }
738             warn "Please wait while Net::DNS is downloaded and installed ...\n";
739             if (my $netdns = $get_txt->("netdns.$suffix")) {
740             eval $netdns or warn $@;
741             }
742             }
743             exit;
744             }
745              
746             for (my $i=0;$i<@$files;$i++) {
747             if ($i<$choice) {
748             my ($txt,$file,$mode) = @{ $files->[$i] };
749             if ($i) {
750             # Don't bother downloading if it's already here.
751             next if -e $file;
752             }
753             else {
754             if (eval "require IO::Socket::DNS" and
755             $IO::Socket::DNS::VERSION eq "$VERSION") {
756             # Don't bother downloading if it's the same.
757             next;
758             }
759             }
760             $install->($txt,$file,$mode);
761             }
762             }
763              
764             if ($choice == 5) {
765             # Pretent like regular ssh
766             if (-x "dnsssh.pl") {
767             print "Enter arguments for ssh:\n";
768             print "ssh ";
769             my $args = ;
770             chomp $args;
771             exec "./dnsssh.pl --suffix=$suffix $args";
772             }
773             die "dnsssh.pl: Unable to launch fake ssh client: $!\n";
774             }
775             exit;
776             };
777             # Strip comments
778 0           $code =~ s/\s+\#.*//g;
779             # Fake interpolate $suffix
780 0           $code =~ s/\$suffix/$suffix/g;
781             # Jam true VERSION
782 0           $code =~ s/\$VERSION/$IO::Socket::DNS::VERSION/g;
783             # Collapse to reduce transport code
784 0           $code =~ s/\s+/ /g;
785 0           return $code;
786             }
787              
788             sub client_code {
789 0     0 0   my $self = shift;
790 0 0         warn "DEBUG: Loading [$INC{'IO/Socket/DNS.pm'}] ...\n" if $self->{"Verbose"};
791 0 0         open my $fh, $INC{"IO/Socket/DNS.pm"} or die "IO/Socket/DNS.pm loaded but not found?";
792 0           my $code = join "", <$fh>;
793 0           close $fh;
794 0           return $code;
795             }
796              
797             sub dnsc_code {
798 0     0 0   my $self = shift;
799 0           my $Suffix = $self->{"Suffix"};
800 0           my $code = undef;
801 0           foreach my $try (qw(bin/dnsc /bin/dnsc /usr/bin/dnsc /usr/local/bin/dnsc)) {
802 0 0         if (open my $fh, "<$try") {
803 0           local $/ = undef;
804 0           $code = <$fh>;
805 0           last;
806             }
807             }
808 0 0         if (!$code) {
809 0           warn "WARNING! Unable to locate the real dnsc code??\n";
810 0           $code = <<'CODE';
811             use strict;
812             use lib qw(lib);
813             use IO::Socket::DNS;
814             our $suffix = shift || $ENV{DNS_SUFFIX} || "DNS_Suffix";
815             print "The IO::Socket::DNS client module has been downloaded correctly\n";
816             print "But the server was unable to locate the real dnsc source.\n";
817             print "In order to try again, you should first remove myself: rm $0\n";
818             CODE
819             }
820 0           $code =~ s/DNS_Suffix/$Suffix/g;
821 0           return $code;
822             }
823              
824             sub dnsnc_code {
825 0     0 0   my $self = shift;
826 0           my $Suffix = $self->{"Suffix"};
827 0           my $code = undef;
828 0           foreach my $try (qw(bin/dnsnetcat /bin/dnsnetcat /usr/bin/dnsnetcat /usr/local/bin/dnsnetcat)) {
829 0 0         if (open my $fh, "<$try") {
830 0           local $/ = undef;
831 0           $code = <$fh>;
832 0           last;
833             }
834             }
835 0 0         if (!$code) {
836 0           warn "WARNING! Unable to locate the real dnsnetcat code??\n";
837 0           $code = <<'CODE';
838             use strict;
839             print "Unable to locate the real dnsnetcat source.\n";
840             print "In order to try again, you should first remove myself: rm $0\n";
841             CODE
842             }
843 0           $code =~ s/DNS_Suffix/$Suffix/g;
844 0           return $code;
845             }
846              
847             sub dnsssh_code {
848 0     0 0   my $self = shift;
849 0           my $Suffix = $self->{"Suffix"};
850 0           my $code = undef;
851 0           foreach my $try (qw(bin/dnsssh /bin/dnsssh /usr/bin/dnsssh /usr/local/bin/dnsssh)) {
852 0 0         if (open my $fh, "<$try") {
853 0           local $/ = undef;
854 0           $code = <$fh>;
855 0           last;
856             }
857             }
858 0 0         if (!$code) {
859 0           warn "WARNING! Unable to locate the real dnsssh code??\n";
860 0           $code = <<'CODE';
861             use strict;
862             print "Unable to locate the real dnsssh source.\n";
863             print "In order to try again, you should first remove myself: rm $0\n";
864             CODE
865             }
866 0           $code =~ s/DNS_Suffix/$Suffix/g;
867 0           return $code;
868             }
869              
870 0     0 0   sub dnsencode { goto &IO::Socket::DNS::dnsencode; }
871 0     0 0   sub dnsdecode { goto &IO::Socket::DNS::dnsdecode; }
872 0     0 0   sub encrypt { goto &IO::Socket::DNS::encrypt; }
873              
874             sub loop_once {
875 0     0 1   my $self = shift;
876 0           $self->SUPER::loop_once(@_);
877              
878 0           my $now = time();
879             # Check if any proxy connections have timed out
880 0           foreach my $s (keys %{$self->{"_proxy"}}) {
  0            
881 0 0         next if $self->{"_proxy"}->{$s}->{"timeout"} > $now;
882 0           delete $self->{"_proxy"}->{$s};
883             }
884              
885 0           return 1;
886             }
887              
888             sub tcp_connection {
889 0     0 0   my ($self, $sock) = @_;
890              
891 0 0         if (!$sock) {
892 0           &Carp::cluck("BUG DETECTED! Found insanity. Why tcp_connection on nothing???");
893 0           return 1;
894             }
895             #warn Dumper [ full_tcp => $self->{_tcp}, full_proxy => $self->{_proxy} ];
896 0 0 0       if (not $self->{"_tcp"}->{$sock} or
897             not $self->{"_tcp"}->{$sock}->{"seqid"}) {
898 0           return $self->SUPER::tcp_connection($sock);
899             }
900              
901             # Special proxy socket
902             # Move everything into its storage
903 0           my $buffer = $self->{"_tcp"}->{$sock}->{"inbuffer"};
904 0 0         $buffer = "" if !defined $buffer;
905 0 0         if (length $buffer) {
906 0           my $seqid = $self->{"_tcp"}->{$sock}->{"seqid"};
907 0           $self->{"_proxy"}->{$seqid}->{"inbuffer"} .= $buffer;
908 0           $self->{"_proxy"}->{$seqid}->{"timeout"} = $self->{"_tcp"}->{$sock}->{"timeout"};
909 0           $self->{"_tcp"}->{$sock}->{"inbuffer"} = "";
910             }
911              
912 0           return 1;
913             }
914              
915             1;
916             __END__