#!/usr/bin/perl # # editdns.pl - EditDNS record management script # # Copyright (c) 2009 Michal Wojciechowski (odyniec.net) # http://odyniec.net/projects/editdns.pl/ # # This script enables you to add/delete DNS records for your domains # hosted at EditDNS.net. # # Usage instructions # ------------------ # # Enter your EditDNS e-mail address and password as $EMAIL and # $PASSWORD below. # # The script reads a sequence of commands from its standard input. Three # commands are recognized: # # Add-Record - add a new DNS record with the specified # set of fields # Delete-Record - delete an existing record that matches the # specified criteria # Delete-All-Records - delete all existing records that match the # specified criteria # # Command parameters are entered as field name, followed by a colon, # then the value enclosed in double quotes (Name:"Value"). Here's an # example of an Add-Record command: # # Add-Record Name:"www.example.com." Type:"A" AUX:"1" TTL:"86400" Data:"12.34.56.78" # # This command adds a new A record named "www.example.com.", with an IP # address of 12.34.56.78. When adding a record, you need to enter all # the required parameters, ie. name, record type, AUX, TTL, and data. # # The Delete-Record command has similar syntax, but in this case the # provided parameters are used to locate the existing DNS record that # will be deleted. It's not necessary to enter all the parameters, you # can just supply one, eg.: # # Delete the record named "mail.example.com.": # Delete-Record Name:"mail.example.com." # # If more than one parameter is provided, the command looks for the # first record that matches ALL the parameters (as if there was a # logical AND), eg.: # # Delete the MX record for example.com: # Delete-Record Name:"example.com." Type:"MX" # # Additionally, you can use regular expressions with Delete-Record. To # use a regular expression, enclose it in double quotes and put a tilde # (~) character in front of it, eg.: # # Delete the example.com A record whose data starts with "192.168": # Delete-Record Name:"example.com." Type:"A" Data:~"^192\.168" # # You can also do negative regular expression matching. Precede the # tilde character with a bang (!), eg.: # # Delete the example.com MX record whose data doesn't start with # "mail.": # Delete-Record Name:"example.com." Type:"MX" Data:!~"^mail\." # # The Delete-All-Records command works the same way as Delete-Record, # but, as the name implies, it deletes all the records that match the # given criteria. # # The script determines the domain that the record belongs to based on # the provided "Name" parameter, but you can enter the domain name # explicitly using the "Domain" parameter, eg.: # # Delete all CNAME records for the example.com domain: # Delete-All-Records Domain:"example.com." Type:"CNAME" # # If you're using a regular expression match for the "Name" parameter, # you MUST supply the domain name explicitly, eg.: # # Delete a record that starts with "www": # Delete-Record Domain:"example.com." Name:~"^www" # use LWP::UserAgent; $EMAIL = 'EMAIL'; $PASSWORD = 'PASSWORD'; # # Add a new record # sub action_add_record { ($options) = @_; %record = ( ); while ($options =~ m/\G\s*(\w+):"((?:\\"|[^"])*?)"/sg) { ($record{lc $1} = $2) =~ s/\\"/"/sg; } $zone = undef; # Find the corresponding domain foreach $domain (keys %domains) { if (substr($record{'name'}, -length($domain)-1) eq $domain . '.') { $zone = $domains{$domain}; last; } } if (defined $zone) { print "Adding record: " . $record{'name'} . " " . $record{'type'} . " " . $record{'data'} . "\n"; %post = ( 'site' => 'modify', 'zone' => $zone, 'newRecordName' => $record{'name'}, 'newRecordType' => $record{'type'}, 'newRecordMX' => $record{'aux'}, 'defaultTTL' => $record{'ttl'}, 'newRecordIP' => $record{'data'}, 'addRecord' => 'Add' ); $ua->post('https://www.editdns.net/index.php', \%post); # # FIXME: Check if successful # } else { # Domain not found print "Error: No matching domain for record name \"" . $record{'name'} . "\".\n"; exit 1; } } # # Delete record/records # sub action_delete_record { ($options, $all) = @_; %tests = ( ); while ($options =~ m/\G\s*(\w+):(\!?~?)"((?:\\"|[^"])*?)"/sg) { ($value = ($2 eq '' ? ' ' : $2) . $3) =~ s/\\"/"/sg; push(@{$tests{lc $1}}, $value); } $zone = undef; if (defined($tests{'domain'})) { # Domain given explicitly ($domain = $tests{'domain'}[0]) =~ s/\s|.$//g; $zone = $domains{$domain}; delete($tests{'domain'}); } else { # Find matching domain based on name foreach $domain (keys %domains) { if (substr($tests{'name'}[0], -length($domain)-1) eq $domain . '.') { $zone = $domains{$domain}; last; } } } if (defined $zone) { # Get current records $resp = $ua->get('https://www.editdns.net/?site=modify&modify=1' . '&zone=' . $zone); $html = $resp->content(); @records = ( ); while ($html =~ m!\G.*? \s*(.*?) \s*(.*?) \s*\s*(\S*?) \s*(.*?) \s*!xsg) { push(@records, { 'id' => $1, 'name' => $2, 'type' => $3, 'aux' => $4, 'ttl' => $5, 'data' => $6 }); } RECORD: foreach $record (@records) { foreach $field (keys %tests) { foreach $test (@{$tests{$field}}) { $test =~ m/^([ ~!]+)(.*)$/s; $type = $1; $pattern = $2; next RECORD if # Exact match ($type eq ' ' && $record->{$field} ne $pattern) || # Regular expression match ($type eq '~' && $record->{$field} !~ $pattern) || # Negative regular expression match ($type eq '!~' && $record->{$field} =~ $pattern); } } print "Deleting record: " . $record->{'name'} . " " . $record->{'type'} . " " . $record->{'data'} . "\n"; %post = ( 'site' => 'modify', 'zone' => $zone, 'recordName2' => $record->{'name'}, 'modify' => '1', 'modifyDetails' => '1', 'deleteRecordID' => $record->{'id'}, 'deleteRecordAgree' => 'Delete Record' ); $ua->post('https://www.editdns.net/index.php', \%post); # If only the first matching record is to be deleted, exit loop last unless $all; } } else { # Domain not found print "Error: No matching domain for record name \"" . $record{'name'} . "\".\n"; exit 1; } } sub action { ($command) = @_; # Is it a comment? return if ($command =~ m/^\s*#/); ($action, $options) = split(/\s+/, $command, 2); $action = lc $action; if ($action eq 'add-record') { action_add_record($options); } elsif ($action eq 'delete-record') { action_delete_record($options, 0); } elsif ($action eq 'delete-all-records') { action_delete_record($options, 1); } } $ua = new LWP::UserAgent; $ua->agent("Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90)"); $ua->cookie_jar({}); $ua->timeout(60); # # Login to EditDNS # %post = ( 'site' => 'login', 'email' => $EMAIL, 'password' => $PASSWORD, 'login' => 'Login' ); $resp = $ua->post('https://www.editdns.net/?site=login', \%post); if ($resp->header("Location") =~ m/errorMessage/s) { print STDERR "Error: login failed.\n"; exit 1; } # # Get the list of domains # $resp = $ua->get('https://www.editdns.net/?site=main'); $html = $resp->content(); %domains = ( ); if ($html =~ m![^<]+
([^<]+)
) { action($cmd); }