PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/dnsreflect.pl

https://bitbucket.org/ethanr/decloak
Perl | 201 lines | 135 code | 38 blank | 28 comment | 13 complexity | 39c6d03bf92addc2013cf2cc1d2a50e2 MD5 | raw file
  1. #!/usr/bin/perl
  2. ###############
  3. use Net::DNS::Nameserver;
  4. use DBD::Pg;
  5. use POSIX ":sys_wait_h";
  6. use strict;
  7. use warnings;
  8. # Configure the user ID to run as (must start as root)
  9. my $user = 1015;
  10. # Configure the interfaces and ports
  11. # This address must have port 53 available and be the DNS server
  12. # for the wildcard subdomain (spy.decloak.net). Changing this
  13. # domain also means updating the Java, Flash, and PHP.
  14. my $serv = '0.0.0.0';
  15. # You need :53 on the wildcard domain and :5353 on the IP running the web site
  16. my $bind = [ [$serv, 53], ['0.0.0.0', 5353] ];
  17. # You need :53530 TCP on the IP running the web site
  18. my $tcps = [ ['0.0.0.0', 53530], ['0.0.0.0', 843] ];
  19. # Wildcard subdomain we handle DNS for
  20. my $dom = "spy.decloak.net";
  21. # Configure postgres credentials
  22. my $db_name = "postgres";
  23. my $db_user = "dbusername";
  24. my $db_pass = "dbpassword";
  25. my $dbh;
  26. my $opts = {
  27. AutoCommit => 1,
  28. RaiseError => 0,
  29. };
  30. # Escape the $dom var to be a valid regex
  31. $dom =~ s/\./\\\./g;
  32. foreach my $c ( @{$bind} ) {
  33. if (! fork()) {
  34. Launch($c->[0], $c->[1]);
  35. exit(0);
  36. }
  37. }
  38. foreach my $c ( @{$tcps} ) {
  39. if (! fork()) {
  40. LaunchTCP($c->[0], $c->[1]);
  41. exit(0);
  42. }
  43. }
  44. exit(0);
  45. # This table must already exist
  46. ##
  47. # Table "public.requests"
  48. # Column | Type | Modifiers
  49. # --------+-----------------------+-----------
  50. # cid | character(32) |
  51. # type | character varying(16) |
  52. # eip | character varying(16) |
  53. # iip | character varying(16) |
  54. # dip | character varying(16) |
  55. # stamp | timestamp |
  56. ##
  57. sub reply_handler {
  58. my ($qname, $qclass, $qtype, $peerhost) = @_;
  59. my ($rcode, @ans, @auth, @add);
  60. if ($qname =~ m/^([a-z0-9]{32})\.(\w+)\.(\d+\.\d+\.\d+\.\d+)\.(\d+\.\d+\.\d+\.\d+)\.$dom/) {
  61. # print "$peerhost > $qname (MATCH)\n";
  62. my ($cid, $type, $eip, $iip, $dip) = ($1, $2, $3, $4, $peerhost);
  63. my $sth = $dbh->prepare("INSERT INTO requests values (?, ?, ?, ?, ?, now())");
  64. $sth->execute($cid, $type, $eip, $iip, $dip);
  65. $sth->finish();
  66. }else{
  67. # print "$peerhost > $qname (NO MATCH)\n";
  68. }
  69. if ($qtype eq "A")
  70. {
  71. my ($ttl, $rdata) = (1, $peerhost);
  72. push @ans, Net::DNS::RR->new("$qname $ttl $qclass A $rdata");
  73. $rcode = "NOERROR";
  74. }
  75. elsif ($qtype eq "PTR") {
  76. my ($ttl, $rdata) = (1, $peerhost);
  77. push @ans, Net::DNS::RR->new("$qname $ttl $qclass A $rdata");
  78. $rcode = "NOERROR";
  79. }
  80. else {
  81. my ($ttl, $rdata) = (1, $peerhost);
  82. push @ans, Net::DNS::RR->new("$qname $ttl $qclass A $rdata");
  83. $rcode = "NOERROR";
  84. }
  85. # mark the answer as authoritive (by setting the 'aa' flag
  86. return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
  87. }
  88. sub Launch {
  89. my $host = shift();
  90. my $port = shift();
  91. $0 .= " ($host:$port)";
  92. $dbh = DBI->connect("DBI:Pg:dbname=$db_name", $db_user, $db_pass, $opts) || die "Couldn't connect to database: " . DBI->errstr;
  93. my $ns = Net::DNS::Nameserver->new(
  94. LocalPort => $port,
  95. LocalAddr => $host,
  96. ReplyHandler => \&reply_handler,
  97. Verbose => 0,
  98. );
  99. $<= $> = $user;
  100. if ($ns) {
  101. $ns->main_loop;
  102. } else {
  103. die "Couldn't create nameserver object\n";
  104. }
  105. }
  106. sub LaunchTCP {
  107. my $host = shift();
  108. my $port = shift();
  109. $0 .= " TCP ($host:$port)";
  110. my $srv = IO::Socket::INET->new(
  111. 'Proto' => 'tcp',
  112. 'LocalPort' => $port,
  113. 'LocalAddr' => $host,
  114. 'Listen' => 5,
  115. 'Reuse' => 1
  116. );
  117. die unless $srv;
  118. $<= $> = $user;
  119. while (my $cli = $srv->accept()) {
  120. my $kid = 0;
  121. # Clean zombies
  122. do {
  123. $kid = waitpid(-1, WNOHANG);
  124. } while $kid > 0;
  125. if(! fork()) {
  126. while(1) {
  127. my $sel = IO::Select->new($cli);
  128. $cli->autoflush(1);
  129. if ($sel->can_read(5)) {
  130. my $buf = "";
  131. my $len = sysread($cli, $buf, 16384);
  132. if ($len && $buf =~ m/^([a-z0-9]{32}):(.*)/i) {
  133. my $cid = $1;
  134. my $eip = $2;
  135. chomp($eip);
  136. $dbh = DBI->connect("DBI:Pg:dbname=$db_name", $db_user, $db_pass, $opts) || die "Couldn't connect to database: " . DBI->errstr;
  137. my $sth = $dbh->prepare("INSERT INTO requests values (?, ?, ?, ?, ?, now())");
  138. $sth->execute($cid, 'socket', $eip, '0.0.0.0', $cli->peerhost);
  139. $sth->finish();
  140. print $cli ($cli->peerhost . "\x00");
  141. last;
  142. }
  143. if($len && $buf eq "<policy-file-request/>\x00") {
  144. print $cli "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\x00";
  145. }
  146. if(!$len || length($buf) == 0) {
  147. last;
  148. }
  149. }
  150. }
  151. $cli->close();
  152. exit(0);
  153. }
  154. }
  155. }