File Coverage

blib/lib/AWS/IP.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1 1     1   908 use strict;
  1         3  
  1         54  
2 1     1   7 use warnings;
  1         2  
  1         60  
3             package AWS::IP;
4             $AWS::IP::VERSION = '0.03';
5 1     1   211 use Cache::File;
  0            
  0            
6             use Carp;
7             use HTTP::Tiny;
8             use JSON::XS;
9             use File::Temp 'tempdir';
10             use Net::CIDR::Set;
11              
12             # required by HTTP::Tiny for https
13             use IO::Socket::SSL 1.56;
14             use Net::SSLeay 1.49;
15              
16             use constant CACHE_KEY => 'AWS_IPS';
17              
18             # ABSTRACT: Get and search AWS IP ranges in a caching, auto-refreshing way
19              
20              
21             sub new ($cache_timeout_seconds, [$cache_path])
22             {
23             croak 'Incorrect number of args passed to AWS::IP->new()' unless @_ >= 2 && @_ <= 3;
24             my ($class, $cache_timeout_secs, $cache_path) = @_;
25              
26             # validate args
27             unless ($cache_timeout_secs
28             && $cache_timeout_secs =~ /^[0-9]+$/)
29             {
30             croak 'Error argument cache_timeout_secs must be a positive integer';
31             }
32              
33             bless {
34             cache => Cache::File->new( cache_root => ($cache_path || tempdir()),
35             lock_level => Cache::File::LOCK_LOCAL(),
36             default_expires => "$cache_timeout_secs sec"),
37             }, $class;
38             }
39              
40              
41             sub ip_is_aws
42             {
43             my ($self, $ip, $service) = @_;
44              
45             croak 'Error must supply an ip address' unless $ip;
46              
47             my $ip_ranges;
48              
49             if ($service)
50             {
51             $ip_ranges = Net::CIDR::Set->new( map { $_->{ip_prefix} } grep { $_->{service} eq $service } @{$self->get_raw_data->{prefixes}});
52             }
53             else
54             {
55             $ip_ranges = Net::CIDR::Set->new( map { $_->{ip_prefix} } @{$self->get_raw_data->{prefixes}} );
56             }
57             $ip_ranges->contains($ip);
58             }
59              
60              
61              
62             sub get_raw_data
63             {
64             my ($self) = @_;
65              
66             my $entry = $self->{cache}->entry(CACHE_KEY);
67              
68             if ($entry->exists)
69             {
70             decode_json($entry->get());
71             }
72             else
73             {
74             decode_json($self->_refresh_cache);
75             }
76             }
77              
78              
79             sub get_cidrs
80             {
81             my ($self) = @_;
82             [ map { $_->{ip_prefix} } @{$self->get_raw_data->{prefixes}} ];
83             }
84              
85              
86             sub get_cidrs_by_region
87             {
88             my ($self, $region) = @_;
89              
90             croak 'Error must provide region' unless $region;
91             [ map { $_->{ip_prefix} } grep { $_->{region} eq $region } @{$self->get_raw_data->{prefixes}} ];
92             }
93              
94              
95             sub get_cidrs_by_service
96             {
97             my ($self, $service) = @_;
98              
99             croak 'Error must provide service' unless $service;
100             [ map { $_->{ip_prefix} } grep { $_->{service} eq $service } @{$self->get_raw_data->{prefixes}} ];
101             }
102              
103              
104             sub get_regions
105             {
106             my ($self) = @_;
107             my %regions;
108             for (@{$self->get_raw_data->{prefixes}})
109             {
110             $regions{ $_->{region} } = 1;
111             }
112             [ keys %regions ];
113             }
114              
115              
116             sub get_services
117             {
118             my ($self) = @_;
119             my %services;
120             for (@{$self->get_raw_data->{prefixes}})
121             {
122             $services{ $_->{service} } = 1;
123             }
124             [ keys %services ];
125             }
126              
127              
128              
129             sub _refresh_cache
130             {
131             my ($self) = @_;
132              
133             my $response = HTTP::Tiny->new->get('https://ip-ranges.amazonaws.com/ip-ranges.json');
134              
135             if ($response->{success})
136             {
137             my $entry = $self->{cache}->entry(CACHE_KEY);
138             $entry->set($response->{content});
139              
140             # return the data
141             $response->{content};
142             }
143             else
144             {
145             croak "Error requesting $response->{url} $response->{code} $response->{reason}";
146             }
147             }
148              
149             sub _refresh_cache_from_string
150             {
151             my ($self, $data) = @_;
152              
153             my $entry = $self->{cache}->entry(CACHE_KEY);
154             $entry->set($data);
155              
156             # return the data
157             $data;
158             }
159             1;
160              
161             __END__