wiki:doc/TorDNSExitList

Note: This service is being replaced with TorBEL

Introduction

It is useful for a variety of reasons to determine if a connection is coming from a Tor node. Early attempts to determine if a given IP address was a Tor exit used the directory to match IP addresses and exit policies. This approach had a number of drawbacks, including false negatives when a Tor router exits traffic from a different IP address than its OR port listens on. The Tor DNS-based Exit List was designed to overcome these problems and provide a simple interface for answering the question: is this a Tor exit?

Implementation

An implementation of the Tor DNS Exit List has been completed at exitlist.torproject.org. DNS queries are answered via this host in the manner described in the design document. The exit nodes are tested regularly to avoid the false negatives when inspecting the directory entries alone.

A web front end for this service is available at check.torproject.org.

Sources for the tordnsel are available at p56soo2ibjkx23xo.onion/darcs/tordnsel. You can sync with the following commands through an http proxy at port 8118 forwarding to Tor:

env http_proxy=http://127.0.0.1:8118/
darcs get http://p56soo2ibjkx23xo.onion/darcs/tordnsel

Examples

The following examples describe how to utilize this service in various ways. Please add to this list if you have implemented hooks for a language or framework not provided below. Toko bunga - Toko bunga jakarta - Tangki fiberglass - Atap fiberglass - Rental forklift

Ruby & Tor.rb

You will need the Tor.rb gem installed for the following to work.

require 'rubygems'
require 'tor'

Tor::DNSEL.include?("208.75.57.100")               #=> true
Tor::DNSEL.include?("1.2.3.4")                     #=> false

PHP Pear Net_DNS

You will need the Pear Net_DNS module for this to work properly:

include("Net/DNS.php");

// torel_check ($ip, $port, $destip) queries the Tor DNS Exit List server.
//   The result of the query is one of the following:
//   -1 : DNS lookup failed to get a response, or other error occurred.
//    0 : $ip does not appear to be a Tor exit.
//    1 : $ip is a known Tor exit for the provided destination IP / port.

function revaddr ($ip) {
  list($a, $b, $c, $d) = split("[.]", $ip);
  return("${d}.${c}.${b}.${a}");
}

function torel_qh ($ip, $port, $destip) {
  $rsrcip = revaddr ($ip);
  $rdstip = revaddr ($destip);
  return("${rsrcip}.${port}.${rdstip}.ip-port.exitlist.torproject.org");
}

function torel_check ($ip, $port, $destip) {
  $ndr = new Net_DNS_Resolver(); 
  $qh = torel_qh($ip, $port, $destip);

  // uncomment these two lines to query the server directly...
  //$ns = "exitlist-ns.torproject.org";
  //$ndr->nameservers( array($ns) );

  // tune DNS params accordingly.  this is just my preference.
  $ndr->retrans = 2;
  $ndr->retry = 3;
  $ndr->usevc = 0;

  // perform DNS query
  if (! $pkt = $ndr->search($qh)) {
    if (strcmp($ndr->errorstring, "NXDOMAIN") == 0) {
      // response but no answer.  does not appear to be Tor exit.
      return (0);
    }
    // search failed: no response or other problem...
    return(-1);
  }
  if (! isset($pkt->answer[0])) {
    // response but no answer section.  does not appear to be Tor exit.
    // (this should only happen when authority sections are provided without answer)
    return(0);
  }
  // is Tor exit
  return(1);
}

// get client request parameters from Apache or equiv server:
$ip = $myip = $myport = 0;
if (isset ($_SERVER["REMOTE_ADDR"])) { $ip = $_SERVER["REMOTE_ADDR"]; }
if (isset ($_SERVER["SERVER_ADDR"])) { $myip = $_SERVER["SERVER_ADDR"]; }
if (isset ($_SERVER["SERVER_PORT"])) { $myport = $_SERVER["SERVER_PORT"]; }

$istor = torel_check($ip, $myport, $myip);

// use $istor as needed for altering page behavior:
if ($istor < 0) {
  // DNS query failed to get an answer
}
elseif ($istor) {
  // Endpoint is a known Tor exit
}
else {
  // Endpoint does not appear to be a Tor exit
}

Perl Net::DNS

You will need the Net::DNS module and its dependencies for this to work properly.

#!/usr/local/bin/perl

use strict;
use warnings;

# query_exitlist($srcip, $dstip, $dstport) queries the Tor DNS Exit List server.
#   The result of the query is one of the following:
#     undef : DNS lookup failed or an unexpected response was received.
#         0 : $srcip does not appear to be a Tor exit.
#         1 : $srcip is a known Tor exit for the provided destination IP / port.

use Getopt::Long;
use Net::DNS::Resolver;

# Construct a DNSEL query from a source address and destination address/port.
# IP addresses should be in dotted-decimal notation.
sub build_query {
  my ($srcip, $dstip, $dstport) = @_;

  # reverse address octets
  ($srcip, $dstip) = map { join '.', reverse split /\./ } $srcip, $dstip;

  "$srcip.$dstport.$dstip.ip-port.exitlist.torproject.org.";
}

sub query_exitlist {
  my ($srcip, $dstip, $dstport) = @_;

  my $res = Net::DNS::Resolver->new;

  # uncomment this line to query the server directly...
  #$res->nameservers("exitlist-ns.torproject.org");

  # tune DNS params accordingly.  this is just my preference.
  $res->retrans(2);
  $res->retry(3);
  $res->usevc(0);

  # perform DNS query
  if (defined(my $pkt = $res->send(build_query $srcip, $dstip, $dstport))) {
    if (grep $_->type eq 'A', $pkt->answer) {
      # an A record was returned: this is a Tor exit node
      return 1;
    } elsif ($pkt->header->rcode eq 'NXDOMAIN') {
      # NXDOMAIN: this is not a Tor exit node
      return 0;
    }
  }

  # the DNS query failed or something unexpected was returned
  return undef;
}

# defaults, get options...
my $srcip = "82.227.101.236";
my $dstip = "1.2.3.4";
my $dstport = 80;
my $pstatus = GetOptions(
 "srcip=s"   => \$srcip,
 "dstip=s"   => \$dstip,
 "dstport=i" => \$dstport
);

$| = 1;
# perform the lookup...
print "Querying Tor DNS Exit List for IP $srcip to destination $dstip at port $dstport ... ";
my $result = query_exitlist $srcip, $dstip, $dstport;
print "Done.\n";

if ($result) {
  print "$srcip is a known Tor exit to $dstip at port $dstport.\n";
} elsif (defined $result) {
  print "$srcip does not appear to be a Tor exit.\n";
} else {
  print "DNS query failed or an unexpected DNS response was received.\n";
  exit 1;
}

Invocation:

# using defaults
torelcheck.pl
Querying Tor DNS Exit List for IP 82.227.101.236 to destination 1.2.3.4 at port 80 ... Done.
82.227.101.236 is a known Tor exit to 1.2.3.4 at port 80.

# using explicit check
torelcheck.pl --srcip 71.111.92.174 --dstip 66.135.40.74 --dstport 80
Querying Tor DNS Exit List for IP 71.111.92.174 to destination 66.135.40.74 at port 80 ... Done.
71.111.92.174 does not appear to be a Tor exit.

Dig command line

The DNS query tool "dig" can also be used to make manual queries as described on the torel page. Remember to reverse the IP address octets in the query sent:

dig 236.101.227.82.80.74.40.135.66.ip-port.exitlist.torproject.org

In the response below, the 127.0.0.2 address in the answer section means this endpoint, 82.227.101.236, is indeed a Tor exit to port 80:

; <<>> DiG 9.3.2 <<>> 236.101.227.82.80.74.40.135.66.ip-port.exitlist.torproject.org
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56277
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;236.101.227.82.80.74.40.135.66.ip-port.exitlist.torproject.org.        IN A

;; ANSWER SECTION:
236.101.227.82.80.74.40.135.66.ip-port.exitlist.torproject.org. 579 IN A 127.0.0.2

;; Query time: 31 msec
;; SERVER: 4.2.2.2#53(4.2.2.2)
;; WHEN: Sun Jun 17 21:57:12 2007
;; MSG SIZE  rcvd: 96
Last modified 6 weeks ago Last modified on Mar 6, 2014 4:39:01 AM