ehloonion / onionmx

Onion delivery, so delicious
198 stars 26 forks source link

No script required for Exim #14

Open mikecardwell opened 7 years ago

mikecardwell commented 7 years ago

Thought you might be interested. The following Exim macro+router allows me to route to systems with _onion-mx srv records without having to use an external script:

ONIONMX = ${map{\
  ${filter\
    {${lookup dnsdb{srv=_onion-mx._tcp.$domain}}}\
    {match{$item}{ 25 \\S+}}\
  }\
}{\
  ${sg{$item}{.+ }{}}\
}}

onionmx:
  driver     = manualroute
  transport  = remote_smtp
  route_data = ONIONMX

Of course, it relies on the system that it is running on to be set up with transparent Tor routing.

immerda commented 7 years ago

So our reason for the external script was, that as long as the system is not transparently routing traffic through Tor (which probably most mailservers don't do, as Tor exit nodes are widely rejected on port 25), exim still tries to lookup route_data that is not an IP (e.g. foobfakf.onion) through standard DNS means. So either you have a local DNS cache, that transparently lookups .onion to Tor DNS or you use our perl-script version.

The question is whether we'd like to add this option for transparently routed systems, somehow in the documention, though we haven't yet such a chapter.

Btw, we would love comments and suggestions on our current router setups https://git-ipuppet.immerda.ch/ibox-modules/tree/ib_exim/files/conf.d/relay/routers#n192

ryancdotorg commented 6 years ago

Haven't tested actually using it with manualroute as route_data, but I wrote something up that does sorted by priority, then randomized by weighted for the SRV records, on the off chance that anyone ever has more than one.

Example complicated records:

# dig +short _onion-mx._tcp.onionmx.r53.ryanc.org SRV
10 0 2525 zero555555555555.onion.
10 20 25 smallbox5a555555.onion.
10 20 25 smallbox5b555555.onion.
10 60 25 bigbox5555555555.onion.
20 0 25 backupbox5555555.onion.

Perl code:

#!/usr/bin/env perl
use warnings;
use strict;

use Net::DNS::Resolver;

sub weighted_rr_shuffle {
  # sort by ascending priority, then do a weighted shuffle within each priority
  return map {$_->[0]} sort {
    $a->[0]->priority <=> $b->[0]->priority || $b->[1] <=> $a->[1]
  } map {
    # https://softwareengineering.stackexchange.com/a/344274
    # Weighted Random Sampling (2005; Efraimidis, Spirakis) 
    # http://utopia.duth.gr/~pefraimi/research/data/2007EncOfAlg.pdf
    # tweaked to randomize order of equal weights
    [$_, ($_->weight ? (rand() ** (1 / $_->weight)) : -1) + rand() * 1e-9]
  } @_;
}

sub onionmx {
  my $domain = shift;
  my $res = Net::DNS::Resolver->new();
  my $query = $res->search('_onion-mx._tcp.'.$domain, 'SRV');

  if ($query) {
    # load all valid resource records with a .onion address
    my @rrlist = grep {
      $_->type eq 'SRV' && $_->target =~ /\A(?:[a-z2-7]{16}|[a-z2-7]{56})\.onion\z/
    } $query->answer;

    # build result for route_data
    return join(' : ', map {
      # exim uses : as a seperator, so it needs to be doubled to escape it
      $_->target . ($_->port == 25 ? '' : '::'.$_->port)
    } weighted_rr_shuffle(@rrlist));
  }

  return '';
}

1;

Calling interactively via exim4 -be:

# exim4 -be
> ${perl{onionmx}{grepular.com}}
grepularmmmiatj7.onion
> ${perl{onionmx}{onionmx.r53.ryanc.org}}
bigbox5555555555.onion : smallbox5b555555.onion : smallbox5a555555.onion : zero555555555555.onion::2525 : backupbox5555555.onion
> ${perl{onionmx}{onionmx.r53.ryanc.org}}
smallbox5b555555.onion : bigbox5555555555.onion : smallbox5a555555.onion : zero555555555555.onion::2525 : backupbox5555555.onion
> ${perl{onionmx}{onionmx.r53.ryanc.org}}
bigbox5555555555.onion : smallbox5a555555.onion : smallbox5b555555.onion : zero555555555555.onion::2525 : backupbox5555555.onion