package Email::SpoofingDemo::API::DNS; use Dancer2; use Email::SpoofingDemo::DNS::ZoneFile qw(get_txt get_zone_file import_zone_file remove_txt set_txt zone_file_path); our $VERSION = '0.1'; my $base_dir = config->{'zone_directory'}; my $dynamic_zones = config->{'dynamic_zones'}; get '/' => sub { return "Welcome"; }; prefix '/zone/:zone' => sub { get '/file' => sub { my $zone = route_parameters->get('zone'); if (zone_file_path($zone, $base_dir)) { my $contents = get_zone_file($zone, $base_dir, $dynamic_zones); return { contents => $contents }; } else { status 'not_found'; return "$zone: not found"; } }; put '/file' => sub { my $zone = route_parameters->get('zone'); my $contents = body_parameters->get("contents"); if (not defined $contents) { status 'bad_request'; return "missing zone file contents"; } my $result = import_zone_file($zone, $base_dir, $contents, $dynamic_zones); if ($result) { return; } else { status 'bad_request'; return "bad zone file"; } }; get '/spf' => sub { my $zone = route_parameters->get('zone'); my @spfs = get_txt($zone, "", qr/v=spf1(\s+|\Z)/i, 1); my %result; for my $rec (@spfs) { $result{$rec->{owner}} = $rec->{txt}; } return \%result; }; put '/spf' => sub { my $zone = route_parameters->get('zone'); my $spf = body_parameters->get('spf'); status (set_txt($zone, "", $spf) ? 'ok' : 'forbidden'); }; del '/spf' => sub { my $zone = route_parameters->get('zone'); status (remove_txt($zone, "") ? 'ok' : 'not_found'); }; get '/domainkey' => sub { my $zone = route_parameters->get('zone'); my @dkims = get_txt($zone, "", qr/^v=DKIM1/, 1); my %domainkeys; for my $rec (@dkims) { if ($rec->{owner} =~ /^(.+)\._domainkey\.(.*)$/i) { my $selector = $1; my $subzone = $2; $domainkeys{$subzone}{$selector} = $rec->{txt}; } } return \%domainkeys; }; get '/dmarc' => sub { my $zone = route_parameters->get('zone'); my @dmarcs = get_txt($zone, "", qr/^v=DMARC1/, 1); my %result; for my $rec (@dmarcs) { if ($rec->{owner} =~ /^_dmarc\.(.*)$/) { my $domain = $1; $result{$domain} = $rec->{txt}; } } return \%result; }; }; any qr{.*} => sub { status 'not_found'; return "Invalid route" }; dance; true;