File Coverage

blib/lib/Net/Magallanes.pm
Criterion Covered Total %
statement 20 179 11.1
branch 0 76 0.0
condition 0 18 0.0
subroutine 7 14 50.0
pod 5 7 71.4
total 32 294 10.8


line stmt bran cond sub pod time code
1             package Net::Magallanes;
2              
3 1     1   69842 use strict;
  1         12  
  1         28  
4 1     1   27 use 5.008_005;
  1         4  
5             our $VERSION = '0.02';
6              
7 1     1   740 use LWP::UserAgent;
  1         47328  
  1         37  
8 1     1   744 use JSON;
  1         10395  
  1         5  
9 1     1   667 use MIME::Base64;
  1         562  
  1         58  
10 1     1   501 use Net::DNS;
  1         107644  
  1         328  
11 1     1   19 use Carp;
  1         6  
  1         7679  
12              
13             sub new {
14 0     0 1   my $this = shift;
15 0           my %params = @_;
16              
17 0           my ($API_KEY, $IN_FILES);
18 0           my $API_BASE = 'https://atlas.ripe.net/api/v2';
19              
20 0   0       my $class = ref($this) || $this;
21              
22 0 0         $API_KEY = $params{'KEY'} if $params{'KEY'};
23 0 0         $IN_FILES = $params{'INFILES'} if $params{'INFILES'};
24              
25             # armar estructura con defaults sensibles
26 0           my $self = {};
27 0           bless $self, $class;
28              
29             # Si no hay KEY igual le damos, pero no podremos crear cosas, solo
30             # consultar.
31 0           $self->{'KEY'} = $API_KEY;
32 0           $self->{'ua'} = LWP::UserAgent->new(timeout => 10);
33 0           $self->{'ua'}->default_header('Content-Type' => 'application/json');
34 0           $self->{'ua'}->default_header('Accept' => 'application/json');
35 0           $self->{'URL'} = $API_BASE;
36              
37 0           $self->{'_CACHE_MSM'} = {};
38              
39 0 0         if ($IN_FILES) {
40 0           my @files = split ',', $IN_FILES;
41 0           my $data;
42 0           foreach my $file (@files) {
43 0 0         open my $fh, '<', $file
44             or croak "Couldn't open file $file: $!";
45 0           local $/ = undef;
46 0           $data = <$fh>;
47 0           close $fh;
48 0           my $result = decode_json $data;
49 0           my $mi = $result->[0]->{msm_id};
50 0           $self->{'_CACHE_MSM'}->{$mi} = $result;
51             }
52             }
53              
54             # Qué puede venir:
55             # timeouts de https request
56             # versión de API
57             # defaults comunes a todo:
58             # - one_off (default true)
59              
60 0           return $self;
61             }
62              
63             sub results {
64 0     0 0   my $self = shift;
65 0           my $msm_id = shift;
66              
67 0           my $result;
68              
69 0 0 0       croak("You must provide the measurement identificator msm_id (only digits)")
70             unless defined $msm_id and $msm_id =~ /^\d+$/;
71              
72             return $self->{'_CACHE_MSM'}->{$msm_id}
73 0 0         if defined $self->{'_CACHE_MSM'}->{$msm_id};
74              
75 0           my $res = $self->{'ua'}->get( $self->{'URL'} .
76             "/measurements/$msm_id/results/" .
77             '?format=json'
78             );
79              
80 0           $self->{'_JSON'} = $res->decoded_content;
81              
82 0 0         if ($res->is_success) {
83 0           $result = decode_json $res->decoded_content;
84             }
85             else {
86 0           $result = 'ERROR: ' . $res->status_line;
87             }
88              
89 0           $self->{'_CACHE_MSM'}->{$msm_id} = $result;
90              
91 0           return $result;
92             }
93              
94             sub json {
95 0     0 0   my $self = shift;
96              
97 0           return $self->{'_JSON'};
98             }
99              
100             sub answers {
101 0     0 1   my $self = shift;
102 0           my $msm_id = shift;
103 0           my $args = shift;
104              
105 0           my %args;
106 0 0         %args = %{$args} if defined $args;
  0            
107              
108 0 0         my $type = (defined($args{'TYPE'}) ? $args{'TYPE'} : 'A');
109 0 0         my $istime = (defined($args{'TIMESTAMP'}) ? $args{'TIMESTAMP'} : 0);
110              
111 0           my $result = results($self, $msm_id);
112              
113 0           my @sal;
114 0           foreach my $resdo (@{$result}) {
  0            
115 0           my $timestamp = $resdo->{'timestamp'};
116 0 0         if ($resdo->{'type'} eq 'dns') {
117 0           my $res_set = $resdo->{'resultset'};
118 0 0         if ($#{$res_set} < 0) {
  0            
119 0           push @{$res_set}, $resdo;
  0            
120             }
121 0           foreach my $dns (@$res_set) {
122 0           my $abuf = $dns->{'result'}->{'abuf'};
123 0 0         next unless $abuf;
124 0           my $dec_buff = decode_base64 $abuf;
125 0 0 0       if(defined $abuf && defined $dec_buff) {
126 0           my ($dns_pack)= new Net::DNS::Packet(\$dec_buff);
127 0           my @ans = $dns_pack->answer;
128 0           foreach my $ans (@ans) {
129 0 0         next unless $ans->type eq $type;
130 0           my $res_ip;
131 0 0         if ($type eq 'A') {
    0          
132 0           $res_ip = $ans->address;
133             }
134             elsif ($type eq 'AAAA') {
135 0           $res_ip = $ans->address_short;
136             }
137             else {
138 0           $res_ip = $ans->string;
139             }
140 0 0         if ($res_ip) {
141 0 0         if ($istime) {
142 0           my @val = ($timestamp, $res_ip);
143 0           push @sal, \@val;
144             }
145             else {
146 0           push @sal, $res_ip;
147             }
148             }
149             }
150             }
151             }
152             }
153             }
154 0           return @sal;
155             }
156              
157             sub nsids {
158 0     0 1   my $self = shift;
159 0           my $msm_id = shift;
160              
161 0           my $result = results($self, $msm_id);
162              
163 0           my @sal;
164 0           foreach my $resdo (@{$result}) {
  0            
165 0 0         if ($resdo->{'type'} eq 'dns') {
166 0           my $res_set = $resdo->{'resultset'};
167 0 0         if ($#{$res_set} < 0) {
  0            
168 0           push @{$res_set}, $resdo;
  0            
169             }
170 0           foreach my $dns (@$res_set) {
171 0           my $abuf = $dns->{'result'}->{'abuf'};
172 0 0         next unless $abuf;
173 0           my $dec_buff = decode_base64 $abuf;
174 0 0 0       if(defined $abuf && defined $dec_buff) {
175 0           my ($dns_pack)= new Net::DNS::Packet(\$dec_buff);
176 0           my @edns = $dns_pack->edns;
177 0           foreach my $edn (@edns) {
178 0           my $res_ip = $edn->option(3);
179 0 0         push @sal, ($res_ip ? $res_ip : 'NULL');
180             }
181             }
182             }
183             }
184             }
185 0           return @sal;
186             }
187              
188             sub rcodes {
189 0     0 1   my $self = shift;
190 0           my $msm_id = shift;
191 0           my $args = shift;
192              
193 0           my %args;
194 0 0         %args = %{$args} if defined $args;
  0            
195              
196 0 0         my $istime = (defined($args{'TIMESTAMP'}) ? $args{'TIMESTAMP'} : 0);
197              
198 0           my $result = results($self, $msm_id);
199              
200 0           my @sal;
201 0           foreach my $resdo (@{$result}) {
  0            
202 0           my $timestamp = $resdo->{'timestamp'};
203 0 0         if ($resdo->{'type'} eq 'dns') {
204 0           my $res_set = $resdo->{'resultset'};
205 0 0         if ($#{$res_set} < 0) {
  0            
206 0           push @{$res_set}, $resdo;
  0            
207             }
208 0           foreach my $dns (@$res_set) {
209 0           my $abuf = $dns->{'result'}->{'abuf'};
210 0 0         next unless $abuf;
211 0           my $dec_buff = decode_base64 $abuf;
212 0 0 0       if(defined $abuf && defined $dec_buff) {
213 0           my ($dns_pack)= new Net::DNS::Packet(\$dec_buff);
214 0           my $header = $dns_pack->header;
215 0 0         if ($istime) {
216 0           my @val = ($timestamp, $header->rcode);
217 0           push @sal, \@val;
218             }
219             else {
220 0           push @sal, $header->rcode;
221             }
222             }
223             }
224             }
225             }
226 0           return @sal;
227             }
228              
229             sub dns {
230 0     0 1   my $self = shift;
231 0           my %params = @_;
232              
233             croak("You must provide at least the query name")
234 0 0         unless defined $params{'name'};
235             croak('You must provide an API key (KEY constructor param) to create measurements')
236 0 0 0       unless defined $self->{'KEY'} and $self->{'KEY'};
237              
238 0 0         my $qtype = defined($params{'type'}) ? $params{'type'} : 'AAAA';
239 0 0         my $nprb = defined($params{'num_prb'}) ? $params{'num_prb'} : 5;
240              
241 0           my %DEFS = (
242             description => 'DNS measurement to ',
243             type => 'dns',
244             query_class => 'IN',
245             timeout => 5000,
246             retry => 0,
247             af => 4,
248             use_macros => 'false',
249             use_probe_resolver => 'true',
250             resolve_on_probe => 'false',
251             set_nsid_bit => 'true',
252             protocol => 'UDP',
253             udp_payload_size => 1232,
254             skip_dns_check => 'false',
255             include_qbuf => 'false',
256             include_abuf => 'true',
257             prepend_probe_id => 'false',
258             set_rd_bit => 'false',
259             set_do_bit => 'true',
260             set_cd_bit => 'false',
261             # start_time
262             # stop_time
263             # interval
264             # target
265             );
266              
267 0           my %PROBES = (
268             type => 'area',
269             value => 'WW',
270             # tags_include => 'system-ipv4-works,system-can-resolve-a',
271             tags_include => 'system-ipv4-works',
272             );
273              
274 0           $PROBES{'requested'} = $nprb;
275              
276 0           $DEFS{'query_argument'} = $params{'name'};
277 0           $DEFS{'query_type'} = $qtype;
278 0           $DEFS{'description'} .= $params{'name'};
279              
280 0           my %ATLASCALL;
281 0           push @{$ATLASCALL{'definitions'}}, \%DEFS;
  0            
282 0           push @{$ATLASCALL{'probes'}}, \%PROBES;
  0            
283              
284 0           $ATLASCALL{'is_oneoff'} = 'true';
285              
286 0           my $json = encode_json \%ATLASCALL;
287              
288             my $res = $self->{'ua'}->post( $self->{'URL'} .
289             '/measurements/' .
290 0           '?key=' . $self->{'KEY'},
291             Content => $json
292             );
293              
294 0 0         if ($res->is_success) {
295 0           my $msmout = $res->decoded_content;
296 0 0         my $msm = $1 if $res->decoded_content =~ /{"measurements":\[(\d+)\]}/;
297              
298 0 0         croak 'Bad measurement id, please check: ' . $res->decoded_content unless $msm;
299              
300 0           return $msm;
301             }
302             else {
303 0           croak 'Could not create a measurement: ' . $res->status_line;
304             }
305             }
306              
307             1;
308              
309             __END__