#!/usr/bin/perl # # lookupip 1.1 # # Copyright (C) 2006-2008, 2019 # Paul E. Jones # # This program will connect to Packetizer's IP address return service and # either tell you your current IP address or return the IP address and country # for an address provided on the command line. # # Usage: lookupip ([a.b.c.d] | [aaaa:bbbb:cccc:dddd]) # (If no IP address, assume your current address.) # # The XML returned by the server is in this form where the IP address might # be either an IPv4 or IPv6 address: # # # #
a.b.c.d
# cc #
# # We implement a small state machine to ensure that we grab the right node, # should the XML schema change in the future. # # Parsing states: # 0 = IDLE # 1 = Non-target tag found # 2 = Target found in state 0 # 3 = Target
found in state 2 # 4 = Target found in state 2 # use LWP; #use NetAddr::IP::Util qw(:all :inet :ipv4 :ipv6 :math); use XML::Parser; # # ValidIP # # Determine if an IP address is valid # sub IsValidIP { my ($address) = @_; if (IsValidIPv4($address) || IsValidIPv6($address)) { return 1; } return 0; } # # ValidIPv4 # # Determine if an IPv4 address is valid # sub IsValidIPv4 { my ($address) = @_; if ($address =~ m/^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])$/) { return 1; } return 0; } # # ValidIPv6 # # Determine if an IPv6 address is valid # sub IsValidIPv6 { my ($address) = @_; if ($address =~ m/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/) { return 1; } return 0; } # XML Initialization routine sub init_handler { # Processing state machine $parsing_state = 0; @parsing_state_stack = (); $ip_address = ""; $country = ""; } # XML Parsing call-back routines sub start_handler { my($expat, $element, %attrs) = @_; # As we find nodes in the parse tree, progress the state machine if (($parsing_state == 0) && ($element eq "IPAddress")) { push(@parsing_state_stack, $parsing_state); $parsing_state = 2; } elsif (($parsing_state == 2) && ($element eq "address")) { push(@parsing_state_stack, $parsing_state); $parsing_state = 3; } elsif (($parsing_state == 2) && ($element eq "country")) { push(@parsing_state_stack, $parsing_state); $parsing_state = 4; } else { # We are not interested in this node push(@parsing_state_stack, $parsing_state); $parsing_state = 1; } } sub char_handler { my ($expat, $chardata) = @_; # If we are processing character data in state 3, it is the IP address # (Note: the concatenation is necessary, as it might be delivered in # pieces by the XML libraries.) if ($parsing_state == 3) { $ip_address = $ip_address . $chardata; } elsif ($parsing_state == 4) { $country = $country . $chardata; } } sub end_handler { my ($expat, $element) = @_; # Since we've reached the end of an element, lets move back to the # previous parsing state. $parsing_state = pop(@parsing_state_stack); } # # MAIN # { # Service URL $url = "http://services.packetizer.com/ipaddress/"; if ($#ARGV > 0) { print "usage: lookupip [ip address]\n"; exit; } elsif ($#ARGV == 0) { $address = $ARGV[0]; if (!ValidIP($address)) { die "Error: invalid IP address\n"; } # Add the IP address as a parameter to the URL $url .= "?address=$address"; } # Define a user agent $ua = LWP::UserAgent->new; # Get the page from the server $response = $ua->get($url, "Accept" => "application/xml"); die "Error: Unable to get IP address: " . $response->status_line . "\n" unless $response->is_success; die "Error: Invalid content type: " . $response->content_type . "\n" unless $response->content_type eq "application/xml"; # Parse the XML message $parser = XML::Parser->new(ErrorContext => 2); $parser->setHandlers(Init => \&init_handler, Start => \&start_handler, End => \&end_handler, Char => \&char_handler); eval { $parser->parse($response->content); }; if ($@) { die "There was an error parsing the XML:\n$@\n"; } # Provide the IP address print "IP Address: $ip_address\n"; print "Country: $country\n"; }