Net-DNS-CloudFlare-DDNS-0.05/0000755000175000017500000000000012236445231014374 5ustar peterpeterNet-DNS-CloudFlare-DDNS-0.05/t/0000755000175000017500000000000012236445231014637 5ustar peterpeterNet-DNS-CloudFlare-DDNS-0.05/t/pod.t0000644000175000017500000000040112235716146015606 0ustar peterpeter#!perl -T use v5.10; use strict; use warnings FATAL => 'all'; use Test::More; # Ensure a recent version of Test::Pod my $min_tp = 1.22; eval "use Test::Pod $min_tp"; plan skip_all => "Test::Pod $min_tp required for testing POD" if $@; all_pod_files_ok(); Net-DNS-CloudFlare-DDNS-0.05/t/manifest.t0000644000175000017500000000050712235716146016641 0ustar peterpeter#!perl -T use v5.10; use strict; use warnings FATAL => 'all'; use Test::More; unless ( $ENV{RELEASE_TESTING} ) { plan( skip_all => "Author tests not required for installation" ); } my $min_tcm = 0.9; eval "use Test::CheckManifest $min_tcm"; plan skip_all => "Test::CheckManifest $min_tcm required" if $@; ok_manifest(); Net-DNS-CloudFlare-DDNS-0.05/t/00-load.t0000644000175000017500000000042012235716146016161 0ustar peterpeter#!perl -T use v5.10; use strict; use warnings FATAL => 'all'; use Test::More; plan tests => 1; BEGIN { use_ok( 'Net::DNS::CloudFlare::DDNS' ) || print "Bail out!\n"; } diag( "Testing Net::DNS::CloudFlare::DDNS $Net::DNS::CloudFlare::DDNS::VERSION, Perl $], $^X" ); Net-DNS-CloudFlare-DDNS-0.05/t/pod-coverage.t0000644000175000017500000000111312235716146017400 0ustar peterpeter#!perl -T use v5.10; use strict; use warnings FATAL => 'all'; use Test::More; # Ensure a recent version of Test::Pod::Coverage my $min_tpc = 1.08; eval "use Test::Pod::Coverage $min_tpc"; plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage" if $@; # Test::Pod::Coverage doesn't require a minimum Pod::Coverage version, # but older versions don't recognize some common documentation styles my $min_pc = 0.18; eval "use Pod::Coverage $min_pc"; plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage" if $@; all_pod_coverage_ok(); Net-DNS-CloudFlare-DDNS-0.05/Changes0000644000175000017500000000044712236445144015677 0ustar peterpeterRevision history for Net-DNS-CloudFlare-DDNS 0.05 6/11/13 Fix docs Prettify code 0.04 6/11/13 Sort out POD properly 0.03 6/11/13 Split into Net::DNS::CloudFlare::DDNS 0.02 30/10/13 Added -v switch 0.01 30/10/13 Part of DDFlare Net-DNS-CloudFlare-DDNS-0.05/Makefile.PL0000644000175000017500000000143312236306602016345 0ustar peterpeteruse v5.10; use strict; use warnings FATAL => 'all'; use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Net::DNS::CloudFlare::DDNS', AUTHOR => q{Peter Roberts }, VERSION_FROM => 'lib/Net/DNS/CloudFlare/DDNS.pm', ABSTRACT_FROM => 'lib/Net/DNS/CloudFlare/DDNS.pm', LICENSE => 'MIT', PL_FILES => {}, MIN_PERL_VERSION => v5.10, CONFIGURE_REQUIRES => { 'ExtUtils::MakeMaker' => 0, }, BUILD_REQUIRES => { 'Test::More' => 0, }, PREREQ_PM => { 'Moo' => 1.003000, 'Carp' => 1.20, 'LWP::UserAgent' => 6.05, 'JSON::Any' => 1.28, 'Readonly' => 1.03 }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => 'Net-DNS-CloudFlare-DDNS-*' }, ); Net-DNS-CloudFlare-DDNS-0.05/MANIFEST0000644000175000017500000000033112236445231015522 0ustar peterpeterChanges lib/Net/DNS/CloudFlare/DDNS.pm Makefile.PL MANIFEST This list of files README t/00-load.t t/manifest.t t/pod-coverage.t t/pod.t META.yml Module meta-data (added by MakeMaker) Net-DNS-CloudFlare-DDNS-0.05/README0000644000175000017500000000420012235716146015255 0ustar peterpeterNet-DNS-CloudFlare-DDNS Provides the functionality to acquire the current external IP address of a machine and update CloudFlare DNS records with this dynamically. This module is primarily designed to be used by App::DDFlare. INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install SUPPORT AND DOCUMENTATION After installing, you can find documentation for this module with the perldoc command. perldoc Net::DNS::CloudFlare::DDNS You can also look for information at: DDFlare http://https://bitbucket.org/pwr22/ddflare/ RT, CPAN's request tracker (report bugs here) http://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-DNS-CloudFlare-DDNS AnnoCPAN, Annotated CPAN documentation http://annocpan.org/dist/Net-DNS-CloudFlare-DDNS CPAN Ratings http://cpanratings.perl.org/d/Net-DNS-CloudFlare-DDNS Search CPAN http://search.cpan.org/dist/Net-DNS-CloudFlare-DDNS/ LICENSE AND COPYRIGHT Copyright (C) 2013 Peter Roberts This program is distributed under the MIT (X11) License: L Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Net-DNS-CloudFlare-DDNS-0.05/lib/0000755000175000017500000000000012236445231015142 5ustar peterpeterNet-DNS-CloudFlare-DDNS-0.05/lib/Net/0000755000175000017500000000000012236445231015670 5ustar peterpeterNet-DNS-CloudFlare-DDNS-0.05/lib/Net/DNS/0000755000175000017500000000000012236445231016314 5ustar peterpeterNet-DNS-CloudFlare-DDNS-0.05/lib/Net/DNS/CloudFlare/0000755000175000017500000000000012236445231020334 5ustar peterpeterNet-DNS-CloudFlare-DDNS-0.05/lib/Net/DNS/CloudFlare/DDNS.pm0000644000175000017500000002375712236445104021437 0ustar peterpeterpackage Net::DNS::CloudFlare::DDNS; use v5.10; use strict; use warnings FATAL => 'all'; use Moo; use Carp; use LWP::UserAgent; use JSON::Any; use Readonly; =head1 NAME Net::DNS::CloudFlare::DDNS - Object orientated Dynamic DNS interface for CloudFlare =head1 VERSION Version 0.05 =cut our $VERSION = '0.05'; =head1 SYNOPSIS Provides an object orientated interface that can be used to dynamically update DNS records on CloudFlare. use Net::DNS::CloudFlare::DDNS; my $ddns = Net::DNS::CloudFlare::DDNS->new( user => $cloudflare_user, apikey => $cloudflare_api_key, zones => $zones ); my $ddns->update(); ... =head1 METHODS =head2 new Create a new Dynamic DNS updater my $ddns = Net::DNS::CloudFlare::DDNS->new( # Required user => $cloudflare_user, apikey => $cloudflare_api_key, zones => $dns_zones, # Optional verbose => $verbosity ); The zones argument must look like the following [ { zone => $zone_name_1, domains => [ $domain_1, ..., $domain_n ] }, ... { zone => $zone_name_n, domains => [ $domain_1, ..., $domain_n ] } ] Each domain must be an A record within that zone, use undef for the zone itself =head2 update Updates CloudFlare DNS with the current IP address if necessary $ddns->update(); =cut # General Cloudflare API details Readonly my $CLOUDFLARE_URL => 'https://www.cloudflare.com/api_json.html'; Readonly my %CLOUDFLARE_API_PARAMS => ( request => 'a', zone => 'z', user => 'email', key => 'tkn', domain => 'name', id => 'id', ip => 'content', type => 'type', ttl => 'ttl' ); # This request edits a record Readonly my $CLOUDFLARE_REQUEST_EDIT => 'rec_edit'; Readonly my $RECORD_TYPE => 'A'; Readonly my $TTL => '1'; sub update { Readonly my $self => shift; # Get current IP address Readonly my $ip => $self->_getIp; # Don't update unless necessary return if defined $self->_ip && $self->_ip eq $ip; say 'Updating IPs' if $self->verbose; # By default we succeed my $succ = 1; # Try to update each zone for my $zone (@{ $self->_zones }) { say "Updating IPs for $zone->{zone}" if $self->verbose; for my $dom (@{ $zone->{domains} }) { Readonly my $IP_UPDATE_ERROR => "IP update failed for $dom->{name} in $zone->{zone} at $CLOUDFLARE_URL: "; say "Updating IP for $dom->{name} in $zone->{zone}" if $self->verbose; # Update IP Readonly my $res => $self->_ua->post($CLOUDFLARE_URL, { $CLOUDFLARE_API_PARAMS{request} => $CLOUDFLARE_REQUEST_EDIT, $CLOUDFLARE_API_PARAMS{type} => $RECORD_TYPE, $CLOUDFLARE_API_PARAMS{ttl} => $TTL, $CLOUDFLARE_API_PARAMS{domain} => $dom->{name}, $CLOUDFLARE_API_PARAMS{zone} => $zone->{zone}, $CLOUDFLARE_API_PARAMS{id} => $dom->{id}, $CLOUDFLARE_API_PARAMS{user} => $self->_user, $CLOUDFLARE_API_PARAMS{key} => $self->_key, $CLOUDFLARE_API_PARAMS{ip} => $ip }); if($res->is_success) { Readonly my $info => JSON::Any->jsonToObj($res->decoded_content); # API call failed if($info->{result} eq 'error') { carp $IP_UPDATE_ERROR, $info->{msg}; $succ = 0; next; } say "Updated IP for $dom->{name} in $zone->{zone} successfully" if $self->verbose; next; } # HTTP request failed carp $IP_UPDATE_ERROR, $res->status_line; # Mark as failure $succ = 0; } } # Update IP if all updates successful, retry next time otherwise $self->_ip($succ ? $ip : undef); } =head2 verbose Accessor for verbose attribute, set to print status information. # Verbosity on $ddns->verbose(1); # Verbosity off $ddns->verbose(undef); # Print current verbosity say $ddns->verbose; =cut has 'verbose' => ( is => 'rw', default => sub { undef }, ); =head2 _ip Accessor for the IP attribute. # Set IP $ddns->_ip($ip); # Get IP my $up = $dds->_ip; =cut =head2 _getIP Trys to grab the current IP from a number of web services # Get current IP my $ip = $ddns->_getIP; =cut # List of http services returning just an IP Readonly my @IP_URLS => map { "http://$_" } ( 'icanhazip.com', 'ifconfig.me/ip', 'curlmyip.com' ); sub _getIp { Readonly my $self => shift; say 'Trying to get current IP' if $self->verbose; # Try each service till we get an IP for my $serviceUrl (@IP_URLS) { say "Trying IP lookup at $serviceUrl" if $self->verbose; Readonly my $res => $self->_ua->get($serviceUrl); if($res->is_success) { # Chop off the newline my $ip = $res->decoded_content; chomp($ip); say "IP lookup at $serviceUrl returned $ip" if $self->verbose; return $ip; } # log this lookup as failing carp "IP lookup at $serviceUrl failed: ", $res->status_line; } # All lookups have failed croak 'Could not lookup IP' } =head2 _getDomainIds Gets and builds a map of domains to IDs for a given zone # Get domain IDs $ddns->_getDomainIds($zone); =cut # This request loads all information on domains in a zone Readonly my $CLOUDFLARE_REQUEST_LOAD_ALL => 'rec_load_all'; sub _getDomainIds { Readonly my $self => shift; Readonly my $zone => shift; Readonly my $IDS_LOOKUP_ERROR => "Domain IDs lookup for $zone failed: "; say "Trying domain IDs lookup for $zone" if $self->verbose; # Query CloudFlare Readonly my $res => $self->_ua->post($CLOUDFLARE_URL, { $CLOUDFLARE_API_PARAMS{request} => $CLOUDFLARE_REQUEST_LOAD_ALL, $CLOUDFLARE_API_PARAMS{zone} => $zone, $CLOUDFLARE_API_PARAMS{key} => $self->_key, $CLOUDFLARE_API_PARAMS{user} => $self->_user }); if($res->is_success) { Readonly my $info => JSON::Any->jsonToObj($res->decoded_content); # Return data unless failure unless($info->{result} eq 'error') { # Get a hash of domain => id my %ids = map { $_->{type} eq 'A' ? ( $_->{name} => $_->{rec_id} ) : () } @{ $info->{response}{recs}{objs} }; say "Domain IDs lookup for $zone successful" if $self->verbose; return %ids; } # API call failed croak $IDS_LOOKUP_ERROR, $info->{msg}; } # HTTP request failed croak $IDS_LOOKUP_ERROR, $res->status_line; } =head2 BUILD Expands subdomains to full domains and attaches domain IDs =cut sub BUILD { my $self = shift; for my $zone (@{ $self->_zones }) { Readonly my $name => $zone->{zone}; Readonly my $domains => $zone->{domains}; Readonly my %ids => $self->_getDomainIds($name); # Decorate domains foreach (0 .. $#$domains) { # Expand subdomains to full domains $domains->[$_] = defined $domains->[$_] ? "$domains->[$_].$name" : $name; my $dom = $domains->[$_]; # Attach domain IDs croak "No domain ID found for $dom in $name" unless defined $ids{$dom}; # Replace with a hash $domains->[$_] = { name => $dom, id => $ids{$dom} }; } } } has '_ip' => ( is => 'rw', default => sub { undef }, init_arg => undef, ); # Read only attributes # Cloudflare credentials has '_user' => ( is => 'ro', required => 1, init_arg => 'user' ); has '_key' => ( is => 'ro', required => 1, init_arg => 'apikey' ); # Cloudflare zones to update has '_zones' => ( is => 'ro', required => 1, init_arg => 'zones' ); Readonly my $USER_AGENT => "DDFlare/$VERSION"; has '_ua' => ( is => 'ro', default => sub { Readonly my $ua => LWP::UserAgent->new; $ua->agent($USER_AGENT); $ua }, init_arg => undef ); =head1 AUTHOR Peter Roberts, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Net::DNS::CloudFlare::DDNS You can also look for information at: =over 4 =item * DDFlare L =item * RT: CPAN's request tracker (report bugs here) L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 LICENSE AND COPYRIGHT Copyright 2013 Peter Roberts. This program is distributed under the MIT (X11) License: L Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =cut 1; # End of Net::DNS::CloudFlare::DDNS Net-DNS-CloudFlare-DDNS-0.05/META.yml0000644000175000017500000000127212236445231015647 0ustar peterpeter--- #YAML:1.0 name: Net-DNS-CloudFlare-DDNS version: 0.05 abstract: Object orientated Dynamic DNS interface author: - Peter Roberts license: MIT distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: Test::More: 0 requires: Carp: 1.2 JSON::Any: 1.28 LWP::UserAgent: 6.05 Moo: 1.003 perl:  Readonly: 1.03 no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.57_05 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4