Parse an IP Address

From Rosetta Code
Jump to: navigation, search
Task
Parse an IP Address
You are encouraged to solve this task according to the task description, using any language you may know.
The purpose of this task is to demonstrate parsing of text-format IP addresses, using IPv4 and IPv6.

Taking the following as inputs:

127.0.0.1 The "localhost" IPv4 address
127.0.0.1:80 The "localhost" IPv4 address, with a specified port (80)
::1 The "localhost" IPv6 address
[::1]:80 The "localhost" IPv6 address, with a specified port (80)
2605:2700:0:3::4713:93e3 Rosetta Code's primary server's public IPv6 address
[2605:2700:0:3::4713:93e3]:80 Rosetta Code's primary server's public IPv6 address, with a specified port (80)

Emit each described IP address as a hexadecimal integer representing the address, the address space, and the port number specified, if any. In languages where variant result types are clumsy, the result should be ipv4 or ipv6 address number, something which says which address space was represented, port number and something that says if the port was specified.

For example 127.0.0.1 has the address number 7F000001 (2130706433 decimal) in the ipv4 address space.  ::ffff:127.0.0.1 represents the same address in the ipv6 address space where it has the address number FFFF7F000001 (281472812449793 decimal). Meanwhile ::1 has address number 1 and serves the same purpose in the ipv6 address space that 127.0.0.1 serves in the ipv4 address space.

Contents

[edit] C

 
#include <string.h>
#include <memory.h>
 
 
static unsigned int _parseDecimal ( const char** pchCursor )
{
unsigned int nVal = 0;
char chNow;
while ( chNow = **pchCursor, chNow >= '0' && chNow <= '9' )
{
//shift digit in
nVal *= 10;
nVal += chNow - '0';
 
++*pchCursor;
}
return nVal;
}
 
 
 
static unsigned int _parseHex ( const char** pchCursor )
{
unsigned int nVal = 0;
char chNow;
while ( chNow = **pchCursor & 0x5f, //(collapses case, but mutilates digits)
(chNow >= ('0'&0x5f) && chNow <= ('9'&0x5f)) ||
(chNow >= 'A' && chNow <= 'F')
)
{
unsigned char nybbleValue;
chNow -= 0x10; //scootch digital values down; hex now offset by x31
nybbleValue = ( chNow > 9 ? chNow - (0x31-0x0a) : chNow );
//shift nybble in
nVal <<= 4;
nVal += nybbleValue;
 
++*pchCursor;
}
return nVal;
}
 
 
 
//Parse a textual IPv4 or IPv6 address, optionally with port, into a binary
//array (for the address, in network order), and an optionally provided port.
//Also, indicate which of those forms (4 or 6) was parsed. Return true on
//success. ppszText must be a nul-terminated ASCII string. It will be
//updated to point to the character which terminated parsing (so you can carry
//on with other things. abyAddr must be 16 bytes. You can provide NULL for
//abyAddr, nPort, bIsIPv6, if you are not interested in any of those
//informations. If we request port, but there is no port part, then nPort will
//be set to 0. There may be no whitespace leading or internal (though this may
//be used to terminate a successful parse.
//Note: the binary address and integer port are in network order.
int ParseIPv4OrIPv6 ( const char** ppszText,
unsigned char* abyAddr, int* pnPort, int* pbIsIPv6 )
{
unsigned char* abyAddrLocal;
unsigned char abyDummyAddr[16];
 
//find first colon, dot, and open bracket
const char* pchColon = strchr ( *ppszText, ':' );
const char* pchDot = strchr ( *ppszText, '.' );
const char* pchOpenBracket = strchr ( *ppszText, '[' );
const char* pchCloseBracket = NULL;
 
 
//we'll consider this to (probably) be IPv6 if we find an open
//bracket, or an absence of dots, or if there is a colon, and it
//precedes any dots that may or may not be there
int bIsIPv6local = NULL != pchOpenBracket || NULL == pchDot ||
( NULL != pchColon && ( NULL == pchDot || pchColon < pchDot ) );
//OK, now do a little further sanity check our initial guess...
if ( bIsIPv6local )
{
//if open bracket, then must have close bracket that follows somewhere
pchCloseBracket = strchr ( *ppszText, ']' );
if ( NULL != pchOpenBracket && ( NULL == pchCloseBracket ||
pchCloseBracket < pchOpenBracket ) )
return 0;
}
else //probably ipv4
{
//dots must exist, and precede any colons
if ( NULL == pchDot || ( NULL != pchColon && pchColon < pchDot ) )
return 0;
}
 
//we figured out this much so far....
if ( NULL != pbIsIPv6 )
*pbIsIPv6 = bIsIPv6local;
 
//especially for IPv6 (where we will be decompressing and validating)
//we really need to have a working buffer even if the caller didn't
//care about the results.
abyAddrLocal = abyAddr; //prefer to use the caller's
if ( NULL == abyAddrLocal ) //but use a dummy if we must
abyAddrLocal = abyDummyAddr;
 
//OK, there should be no correctly formed strings which are miscategorized,
//and now any format errors will be found out as we continue parsing
//according to plan.
if ( ! bIsIPv6local ) //try to parse as IPv4
{
//4 dotted quad decimal; optional port if there is a colon
//since there are just 4, and because the last one can be terminated
//differently, I'm just going to unroll any potential loop.
unsigned char* pbyAddrCursor = abyAddrLocal;
unsigned int nVal;
const char* pszTextBefore = *ppszText;
nVal =_parseDecimal ( ppszText ); //get first val
if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText ) //must be in range and followed by dot and nonempty
return 0;
*(pbyAddrCursor++) = (unsigned char) nVal; //stick it in addr
++(*ppszText); //past the dot
 
pszTextBefore = *ppszText;
nVal =_parseDecimal ( ppszText ); //get second val
if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText )
return 0;
*(pbyAddrCursor++) = (unsigned char) nVal;
++(*ppszText); //past the dot
 
pszTextBefore = *ppszText;
nVal =_parseDecimal ( ppszText ); //get third val
if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText )
return 0;
*(pbyAddrCursor++) = (unsigned char) nVal;
++(*ppszText); //past the dot
 
pszTextBefore = *ppszText;
nVal =_parseDecimal ( ppszText ); //get fourth val
if ( nVal > 255 || pszTextBefore == *ppszText ) //(we can terminate this one in several ways)
return 0;
*(pbyAddrCursor++) = (unsigned char) nVal;
 
if ( ':' == **ppszText && NULL != pnPort ) //have port part, and we want it
{
unsigned short usPortNetwork; //save value in network order
++(*ppszText); //past the colon
pszTextBefore = *ppszText;
nVal =_parseDecimal ( ppszText );
if ( nVal > 65535 || pszTextBefore == *ppszText )
return 0;
((unsigned char*)&usPortNetwork)[0] = ( nVal & 0xff00 ) >> 8;
((unsigned char*)&usPortNetwork)[1] = ( nVal & 0xff );
*pnPort = usPortNetwork;
return 1;
}
else //finished just with ip address
{
if ( NULL != pnPort )
*pnPort = 0; //indicate we have no port part
return 1;
}
}
else //try to parse as IPv6
{
unsigned char* pbyAddrCursor;
unsigned char* pbyZerosLoc;
int bIPv4Detected;
int nIdx;
//up to 8 16-bit hex quantities, separated by colons, with at most one
//empty quantity, acting as a stretchy run of zeroes. optional port
//if there are brackets followed by colon and decimal port number.
//A further form allows an ipv4 dotted quad instead of the last two
//16-bit quantities, but only if in the ipv4 space ::ffff:x:x .
if ( NULL != pchOpenBracket ) //start past the open bracket, if it exists
*ppszText = pchOpenBracket + 1;
pbyAddrCursor = abyAddrLocal;
pbyZerosLoc = NULL; //if we find a 'zero compression' location
bIPv4Detected = 0;
for ( nIdx = 0; nIdx < 8; ++nIdx ) //we've got up to 8 of these, so we will use a loop
{
const char* pszTextBefore = *ppszText;
unsigned nVal =_parseHex ( ppszText ); //get value; these are hex
if ( pszTextBefore == *ppszText ) //if empty, we are zero compressing; note the loc
{
if ( NULL != pbyZerosLoc ) //there can be only one!
{
//unless it's a terminal empty field, then this is OK, it just means we're done with the host part
if ( pbyZerosLoc == pbyAddrCursor )
{
--nIdx;
break;
}
return 0; //otherwise, it's a format error
}
if ( ':' != **ppszText ) //empty field can only be via :
return 0;
if ( 0 == nIdx ) //leading zero compression requires an extra peek, and adjustment
{
++(*ppszText);
if ( ':' != **ppszText )
return 0;
}
 
pbyZerosLoc = pbyAddrCursor;
++(*ppszText);
}
else
{
if ( '.' == **ppszText ) //special case of ipv4 convenience notation
{
//who knows how to parse ipv4? we do!
const char* pszTextlocal = pszTextBefore; //back it up
unsigned char abyAddrlocal[16];
int bIsIPv6local;
int bParseResultlocal = ParseIPv4OrIPv6 ( &pszTextlocal, abyAddrlocal, NULL, &bIsIPv6local );
*ppszText = pszTextlocal; //success or fail, remember the terminating char
if ( ! bParseResultlocal || bIsIPv6local ) //must parse and must be ipv4
return 0;
//transfer addrlocal into the present location
*(pbyAddrCursor++) = abyAddrlocal[0];
*(pbyAddrCursor++) = abyAddrlocal[1];
*(pbyAddrCursor++) = abyAddrlocal[2];
*(pbyAddrCursor++) = abyAddrlocal[3];
++nIdx; //pretend like we took another short, since the ipv4 effectively is two shorts
bIPv4Detected = 1; //remember how we got here for further validation later
break; //totally done with address
}
 
if ( nVal > 65535 ) //must be 16 bit quantity
return 0;
*(pbyAddrCursor++) = nVal >> 8; //transfer in network order
*(pbyAddrCursor++) = nVal & 0xff;
if ( ':' == **ppszText ) //typical case inside; carry on
{
++(*ppszText);
}
else //some other terminating character; done with this parsing parts
{
break;
}
}
}
 
//handle any zero compression we found
if ( NULL != pbyZerosLoc )
{
int nHead = (int)( pbyZerosLoc - abyAddrLocal ); //how much before zero compression
int nTail = nIdx * 2 - (int)( pbyZerosLoc - abyAddrLocal ); //how much after zero compression
int nZeros = 16 - nTail - nHead; //how much zeros
memmove ( &abyAddrLocal[16-nTail], pbyZerosLoc, nTail ); //scootch stuff down
memset ( pbyZerosLoc, 0, nZeros ); //clear the compressed zeros
}
 
//validation of ipv4 subspace ::ffff:x.x
if ( bIPv4Detected )
{
static const unsigned char abyPfx[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
if ( 0 != memcmp ( abyAddrLocal, abyPfx, sizeof(abyPfx) ) )
return 0;
}
 
//close bracket
if ( NULL != pchOpenBracket )
{
if ( ']' != **ppszText )
return 0;
++(*ppszText);
}
 
if ( ':' == **ppszText && NULL != pnPort ) //have port part, and we want it
{
const char* pszTextBefore;
unsigned int nVal;
unsigned short usPortNetwork; //save value in network order
++(*ppszText); //past the colon
pszTextBefore = *ppszText;
pszTextBefore = *ppszText;
nVal =_parseDecimal ( ppszText );
if ( nVal > 65535 || pszTextBefore == *ppszText )
return 0;
((unsigned char*)&usPortNetwork)[0] = ( nVal & 0xff00 ) >> 8;
((unsigned char*)&usPortNetwork)[1] = ( nVal & 0xff );
*pnPort = usPortNetwork;
return 1;
}
else //finished just with ip address
{
if ( NULL != pnPort )
*pnPort = 0; //indicate we have no port part
return 1;
}
}
 
}
 
 
//simple version if we want don't care about knowing how much we ate
int ParseIPv4OrIPv6_2 ( const char* pszText,
unsigned char* abyAddr, int* pnPort, int* pbIsIPv6 )
{
const char* pszTextLocal = pszText;
return ParseIPv4OrIPv6 ( &pszTextLocal, abyAddr, pnPort, pbIsIPv6);
}
 

Test:

 
#include <stdio.h>
 
... (above code for ParseIPv4OrIPv6 goes here) ...
 
unsigned short htons ( unsigned short us )
{
return ( ((unsigned char*)&us)[0] << 8 ) + ((unsigned char*)&us)[1];
}
 
void dumpbin ( unsigned char* pbyBin, int nLen )
{
int i;
for ( i = 0; i < nLen; ++i )
{
printf ( "%02x", pbyBin[i] );
}
}
 
 
void testcase ( const char* pszTest )
{
unsigned char abyAddr[16];
int bIsIPv6;
int nPort;
int bSuccess;
 
printf ( "Test case '%s'\n", pszTest );
const char* pszTextCursor = pszTest;
bSuccess = ParseIPv4OrIPv6 ( &pszTextCursor, abyAddr, &nPort, &bIsIPv6 );
if ( ! bSuccess )
{
printf ( "parse failed, at about index %d; rest: '%s'\n", pszTextCursor - pszTest, pszTextCursor );
return;
}
 
printf ( "addr: " );
dumpbin ( abyAddr, bIsIPv6 ? 16 : 4 );
printf ( "\n" );
if ( 0 == nPort )
printf ( "port absent" );
else
printf ( "port:  %d", htons ( nPort ) );
printf ( "\n\n" );
 
}
 
 
 
int main ( int argc, char* argv[] )
{
 
//The "localhost" IPv4 address
testcase ( "127.0.0.1" );
 
//The "localhost" IPv4 address, with a specified port (80)
testcase ( "127.0.0.1:80" );
//The "localhost" IPv6 address
testcase ( "::1" );
//The "localhost" IPv6 address, with a specified port (80)
testcase ( "[::1]:80" );
//Rosetta Code's primary server's public IPv6 address
testcase ( "2605:2700:0:3::4713:93e3" );
//Rosetta Code's primary server's public IPv6 address, with a specified port (80)
testcase ( "[2605:2700:0:3::4713:93e3]:80" );
 
//ipv4 space
testcase ( "::ffff:192.168.173.22" );
//ipv4 space with port
testcase ( "[::ffff:192.168.173.22]:80" );
//trailing compression
testcase ( "1::" );
//trailing compression with port
testcase ( "[1::]:80" );
//'any' address compression
testcase ( "::" );
//'any' address compression with port
testcase ( "[::]:80" );
 
return 0;
}
 

Output:

Test case '127.0.0.1'
addr:  7f000001
port absent

Test case '127.0.0.1:80'
addr:  7f000001
port:  80

Test case '::1'
addr:  00000000000000000000000000000001
port absent

Test case '[::1]:80'
addr:  00000000000000000000000000000001
port:  80

Test case '2605:2700:0:3::4713:93e3'
addr:  260527000000000300000000471393e3
port absent

Test case '[2605:2700:0:3::4713:93e3]:80'
addr:  260527000000000300000000471393e3
port:  80

Test case '::ffff:192.168.173.22'
addr:  00000000000000000000ffffc0a8ad16
port absent

Test case '[::ffff:192.168.173.22]:80'
addr:  00000000000000000000ffffc0a8ad16
port:  80

Test case '1::'
addr:  00010000000000000000000000000000
port absent

Test case '[1::]:80'
addr:  00010000000000000000000000000000
port:  80

Test case '::'
addr:  00000000000000000000000000000000
port absent

Test case '[::]:80'
addr:  00000000000000000000000000000000
port:  80

[edit] Go

package main
 
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
)
 
func ParseIPPort(s string) (ip net.IP, port, space string, err error) {
ip = net.ParseIP(s)
if ip == nil {
var host string
host, port, err = net.SplitHostPort(s)
if err != nil {
return
}
if port != "" {
// This check only makes sense if service names are not allowed
if _, err = strconv.ParseUint(port, 10, 16); err != nil {
return
}
}
ip = net.ParseIP(host)
}
if ip == nil {
err = errors.New("invalid address format")
} else {
space = "IPv6"
if ip4 := ip.To4(); ip4 != nil {
space = "IPv4"
ip = ip4
}
}
return
}
 
func main() {
var testCases = []string{
"127.0.0.1",
"127.0.0.1:80",
"::1",
"[::1]:80",
"2605:2700:0:3::4713:93e3",
"[2605:2700:0:3::4713:93e3]:80",
}
max := len("Input")
for _, addr := range testCases {
if len(addr) > max {
max = len(addr)
}
}
fmt.Printf("%-*s  %*s  %-6s %s\n", max, "Input",
2*net.IPv6len, "Address", "Space", "Port")
for _, addr := range testCases {
fmt.Printf("%-*s ", max, addr)
ip, port, space, err := ParseIPPort(addr)
if err != nil {
fmt.Println(err)
continue
}
fmt.Print(strings.Repeat(" ", net.IPv6len-len(ip)))
for _, b := range ip {
fmt.Printf("%02x", b)
}
fmt.Printf("  %-6s %s\n", space, port)
}
}
Output:
Input                                                   Address  Space  Port
127.0.0.1                                              7f000001  IPv4   
127.0.0.1:80                                           7f000001  IPv4   80
::1                            00000000000000000000000000000001  IPv6   
[::1]:80                       00000000000000000000000000000001  IPv6   80
2605:2700:0:3::4713:93e3       260527000000000300000000471393e3  IPv6   
[2605:2700:0:3::4713:93e3]:80  260527000000000300000000471393e3  IPv6   80

[edit] Icon and Unicon

link printf, hexcvt
 
procedure main()
L := ["192.168.0.1", # private
"127.0.0.1", # loop back
"127.0.0.1:80", # loop back +port
"2001:db8:85a3:0:0:8a2e:370:7334", # doc, IPv6 for 555-1234
"2001:db8:85a3::8a2e:370:7334", # doc
"::1", # loop back
"[::1]:80", # loop back +port
"::", # unspecified
"::ffff:192.168.0.1", # transition
"2605:2700:0:3::4713:93e3", # RC
"[2605:2700:0:3::4713:93e3]:80", # RC
"::ffff:71.19.147.227", # RC transition
"[::ffff:71.19.147.227]:80", # RC transition +port
"[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443", # doc +port
"256.0.0.0", # invalid
"g::1"] # invalid
 
every x := !L do {
if x ? (ip := ipmatch(), port := portmatch(), pos(0)) then {
if i := IPv4decode(ip) then
printf("%s is the IPv4 address = x'%s'",x,i)
else if i := IPv6decode(ip) then
printf("%s is the IPv6 address = x'%s'",x,i)
else {
printf("%s is not a valid IP address\n",x)
next
}
if \port then printf(" port=%s\n",port) else printf("\n")
}
else printf("%s is not an IP address\n",x)
}
end
 
 
procedure ipmatch() #: match an ip v4/v6 address
static c4,c6
initial {
c4 := &digits ++ '.'
c6 := &digits ++ 'abcdef:'
}
suspend (="[" || ( (="::ffff:" || tab(many(c4))) | tab(many(c6)) ) || ="]") |
( ="::ffff:" || tab(many(c4))) | tab(many(c6|c4))
end
 
procedure portmatch() #: match a port number
return (=":",0 < (65536 > tab(many(&digits)))) | &null
end
 
procedure IPv4decode(s) #: match IPv4 to hex string
s ? ( ip := (0 <= (256 > tab(many(&digits)))), ip *:= 256, =".",
ip +:= (0 <= (256 > tab(many(&digits)))), ip *:= 256, =".",
ip +:= (0 <= (256 > tab(many(&digits)))), ip *:= 256, =".",
ip +:= (0 <= (256 > tab(many(&digits)))),
return right(hexstring(ip,,&lcase),8) )
end
 
procedure IPv6decode(s) #: IPv6 to hex string
s ?:= 2(="[", tab(-1), ="]") # remove any []
if find(".",s) then # transitional
s ? ( tab(many(':0')), ="ffff:",
return right("ffff" || IPv4decode(tab(0)),32,"0") )
else {
h := t := ""
s ? {
while x := tab(find(":")) do { # head
if *x <= 4 then h ||:= right(x,4,"0")
if ="::" then break
else move(1)
}
while x := tab(find(":")|0) do { # tail
if *x <= 4 then t ||:= right(x,4,"0")
move(1) | break
}
if x := h || repl("0",32-(*h+*t)) || t then # and insides
return x
}
}
end

printf.icn provides the printf family hexcvt.icn provides hex and hexstring

Output:
192.168.0.1 is the IPv4 address = x'c0a80001'
127.0.0.1 is the IPv4 address = x'7f000001'
127.0.0.1:80 is the IPv4 address = x'7f000001' port=80
2001:db8:85a3:0:0:8a2e:370:7334 is the IPv6 address = x'20010db885a3000000008a2e03707334'
2001:db8:85a3::8a2e:370:7334 is the IPv6 address = x'20010db885a3000000008a2e03707334'
::1 is the IPv6 address = x'00000000000000000000000000000001'
[::1]:80 is the IPv6 address = x'00000000000000000000000000000001' port=80
:: is the IPv6 address = x'00000000000000000000000000000000'
::ffff:192.168.0.1 is the IPv6 address = x'00000000000000000000ffffc0a80001'
2605:2700:0:3::4713:93e3 is the IPv6 address = x'260527000000000300000000471393e3'
[2605:2700:0:3::4713:93e3]:80 is the IPv6 address = x'260527000000000300000000471393e3' port=80
::ffff:71.19.147.227 is the IPv6 address = x'00000000000000000000ffff471393e3'
[::ffff:71.19.147.227]:80 is the IPv6 address = x'00000000000000000000ffff471393e3' port=80
[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443 is the IPv6 address = x'20010db885a308d313198a2e03707348' port=443
256.0.0.0 is not a valid IP address
g::1 is not an IP address

[edit] J

Implementation:

parseaddr=:3 :0
if. '.' e. y do.
if. +./'::' E. y do.
parsehybrid y
else.
parseipv4 y
end.
else.
parseipv6 y
end.
)
 
parseipv4=:3 :0
'addr port'=. 2{.<;._2 y,'::'
4,((4#256)#._&".;._1'.',addr),_".port
)
 
parseipv6=:3 :0
'addr port'=. 2{.<;._2 (y-.'['),']]'
split=. I. '::' E. addr
a1=. 8{. dfh;._2 (split {. addr),8#':'
a2=._8{. dfh;._1 (8#':'),split }. addr
6,(65536x#.a1+a2),_".port-.':'
)
 
parsehybrid=:3 :0
'kludge port'=. 2{.<;._2 (tolower y-.'['),']]'
addr=. _1 {:: <;._2 kludge,':'
assert. (kludge-:'::ffff:',addr) +. kludge-: '::',addr
6,(16bffff00000000+1{parseipv4 addr),_".port-.':'
)
 
fmt=:3 :0
port=. ''
((#y){.'v';'addr';'port')=. y
'ipv',(":v),' ',(hfd addr),(#port)#' ',":port
)

Task examples:

   fmt parseaddr '127.0.0.1'
ipv4 7f000001
fmt parseaddr '127.0.0.1:80'
ipv4 7f000001 80
fmt parseaddr '::1'
ipv6 1
fmt parseaddr '[::1]:80'
ipv6 1 80
fmt parseaddr '2605:2700:0:3::4713:93e3'
ipv6 260527000000000300000000471393e3
fmt parseaddr '[2605:2700:0:3::4713:93e3]:80'
ipv6 260527000000000300000000471393e3 80

[edit] Perl

sub parse_v4 {
my ($ip, $port) = @_;
my @quad = split(/\./, $ip);
 
return unless @quad == 4;
for (@quad) { return if ($_ > 255) }
 
if (!length $port) { $port = -1 }
elsif ($port =~ /^(\d+)$/) { $port = $1 }
else { return }
 
my $h = join '' => map(sprintf("%02x", $_), @quad);
return $h, $port
}
 
sub parse_v6 {
my $ip = shift;
my $omits;
 
return unless $ip =~ /^[\da-f:.]+$/i; # invalid char
 
$ip =~ s/^:/0:/;
$omits = 1 if $ip =~ s/::/:z:/g;
return if $ip =~ /z.*z/; # multiple omits illegal
 
my $v4 = '';
my $len = 8;
 
if ($ip =~ s/:((?:\d+\.){3}\d+)$//) {
# hybrid 4/6 ip
($v4) = parse_v4($1) or return;
$len -= 2;
 
}
# what's left should be v6 only
return unless $ip =~ /^[:a-fz\d]+$/i;
 
my @h = split(/:/, $ip);
return if @h + $omits > $len; # too many segments
 
@h = map( $_ eq 'z' ? (0) x ($len - @h + 1) : ($_), @h);
return join('' => map(sprintf("%04x", hex($_)), @h)).$v4;
}
 
sub parse_ip {
my $str = shift;
$str =~ s/^\s*//;
$str =~ s/\s*$//;
 
if ($str =~ s/^((?:\d+\.)+\d+)(?::(\d+))?$//) {
return 'v4', parse_v4($1, $2);
}
 
my ($ip, $port);
if ($str =~ /^\[(.*?)\]:(\d+)$/) {
$port = $2;
$ip = parse_v6($1);
} else {
$port = -1;
$ip = parse_v6($str);
}
 
return unless $ip;
return 'v6', $ip, $port;
}
 
for (qw/127.0.0.1 127.0.0.1:80
::1
[::1]:80
2605:2700:0:3::4713:93e3
[2605:2700:0:3::4713:93e3]:80
::ffff:192.168.0.1
[::ffff:192.168.0.1]:22
::ffff:127.0.0.0.1
a::b::1/)
{
print "$_\n\t";
my ($ver, $ip, $port) = parse_ip($_)
or print "parse error\n" and next;
 
print "$ver $ip\tport $port\n\n";
}
output
127.0.0.1
v4 7f000001 port -1
 
127.0.0.1:80
v4 7f000001 port 80
 
::1
v6 00000000000000000000000000000001 port -1
 
[::1]:80
v6 00000000000000000000000000000001 port 80
 
2605:2700:0:3::4713:93e3
v6 260527000000000300000000471393e3 port -1
 
[2605:2700:0:3::4713:93e3]:80
v6 260527000000000300000000471393e3 port 80
 
::ffff:192.168.0.1
v6 00000000000000000000ffffc0a80001 port -1
 
[::ffff:192.168.0.1]:22
v6 00000000000000000000ffffc0a80001 port 22
 
::ffff:127.0.0.0.1
parse error
a::b::1
parse error

[edit] Perl 6

grammar IP_Addr {
token TOP { ^ [ <IPv4> | <IPv6> ] $ }
 
token IPv4 {
[ <d8> +% '.' ] <?{ $<d8> == 4 }> <port>?
{ @*by8 = @$<d8> }
}
 
token IPv6 {
| <ipv6>
| '[' <ipv6> ']' <port>
}
 
token ipv6 {
| <h16> +% ':' <?{ $<h16> == 8 }>
{ @*by16 = @$<h16> }
 
| [ (<h16>) +% ':']? '::' (<h16>) +% ':' <?{ @$0 + @$1 <= 8 }>
{ @*by16 = @$0, '0' xx 8 - (@$0 + @$1), @$1 }
 
| '::ffff:' <IPv4>
{ @*by16 = '0' xx 5, 'ffff', by8to16 @*by8 }
}
 
token d8 { (\d+) <?{ $0 < 256 }> }
token d16 { (\d+) <?{ $0 < 65536 }> }
token h16 { (<:hexdigit>+) <?{ @$0 <= 4 }> }
 
token port {
':' <d16> { $*port = +$<d16> }
}
}
 
sub by8to16 (@m) { gather for @m -> $a,$b { take ($a * 256 + $b).fmt("%04x") } }
 
my @cases = <
127.0.0.1
127.0.0.1:80
::1
[::1]:80
2605:2700:0:3::4713:93e3
[2605:2700:0:3::4713:93e3]:80
2001:db8:85a3:0:0:8a2e:370:7334
2001:db8:85a3::8a2e:370:7334
[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443
192.168.0.1
::ffff:192.168.0.1
::ffff:71.19.147.227
[::ffff:71.19.147.227]:80
::
256.0.0.0
g::1
0000
0000:0000
0000:0000:0000:0000:0000:0000:0000:0000
0000:0000:0000::0000:0000
0000::0000::0000:0000
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
ffff:ffff:ffff:fffg:ffff:ffff:ffff:ffff
fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
fff:ffff:0:ffff:ffff:ffff:ffff:ffff
>;
 
for @cases -> $addr {
my @*by8;
my @*by16;
my $*port;
 
IP_Addr.parse($addr);
 
say $addr;
if @*by16 {
say " IPv6: ", @*by16».map({:16(~$_)})».fmt("%04x").join;
say " Port: ", $*port if $*port;
}
elsif @*by8 {
say " IPv4: ", @*by8».fmt("%02x").join;
say " Port: ", $*port if $*port;
}
else {
say " BOGUS!";
}
say '';
}
Output:
127.0.0.1
  IPv4: 7f000001

127.0.0.1:80
  IPv4: 7f000001
  Port: 80

::1
  IPv6: 00000000000000000000000000000001

[::1]:80
  IPv6: 00000000000000000000000000000001
  Port: 80

2605:2700:0:3::4713:93e3
  IPv6: 260527000000000300000000471393e3

[2605:2700:0:3::4713:93e3]:80
  IPv6: 260527000000000300000000471393e3
  Port: 80

2001:db8:85a3:0:0:8a2e:370:7334
  IPv6: 20010db885a3000000008a2e03707334

2001:db8:85a3::8a2e:370:7334
  IPv6: 20010db885a3000000008a2e03707334

[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443
  IPv6: 20010db885a308d313198a2e03707348
  Port: 443

192.168.0.1
  IPv4: c0a80001

::ffff:192.168.0.1
  IPv6: 00000000000000000000ffffc0a80001

::ffff:71.19.147.227
  IPv6: 00000000000000000000ffff471393e3

[::ffff:71.19.147.227]:80
  IPv6: 00000000000000000000ffff471393e3
  Port: 80

::
  BOGUS!

256.0.0.0
  BOGUS!

g::1
  BOGUS!

0000
  BOGUS!

0000:0000
  BOGUS!

0000:0000:0000:0000:0000:0000:0000:0000
  IPv6: 00000000000000000000000000000000

0000:0000:0000::0000:0000
  IPv6: 00000000000000000000000000000000

0000::0000::0000:0000
  IPv6: 00000000000000000000000000000000

ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
  IPv6: ffffffffffffffffffffffffffffffff

ffff:ffff:ffff:fffg:ffff:ffff:ffff:ffff
  BOGUS!

fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
  IPv6: 0fffffffffffffffffffffffffffffff

fff:ffff:0:ffff:ffff:ffff:ffff:ffff
  IPv6: 0fffffff0000ffffffffffffffffffff

[edit] PicoLisp

# Return a cons pair of address and port: (address . port)
(de ipAddress (Adr)
(use (@A @B @C @D @Port)
(cond
((match '("[" @A "]" ":" @Port) Adr)
(adrIPv6 (split @A ":") @Port) )
((match '("[" @A "]") Adr)
(adrIPv6 (split @A ":")) )
((match '(@A ":" @B ":" @C) Adr)
(adrIPv6 (cons @A @B (split @C ":"))) )
((match '(@A "." @B "." @C "." @D ":" @Port) Adr)
(adrIPv4 (list @A @B @C @D) @Port) )
((match '(@A "." @B "." @C "." @D) Adr)
(adrIPv4 (list @A @B @C @D)) )
(T (quit "Bad IP address" (pack Adr))) ) ) )
 
(de adrIPv4 (Lst Port)
(cons
(sum >> (-24 -16 -8 0) (mapcar format Lst))
(format Port) ) )
 
(de adrIPv6 (Lst Port)
(cons
(sum >>
(-112 -96 -80 -64 -48 -32 -16 0)
(mapcan
'((X)
(if X
(cons (hex X))
(need (- 9 (length Lst)) 0) ) ) # Handle '::'
(cons (or (car Lst) "0") (cdr Lst)) ) )
(format Port) ) )

Test:

(for A
(quote
"127.0.0.1"
"127.0.0.1:80"
"::1"
"[::1]:80"
"2605:2700:0:3::4713:93e3"
"[2605:2700:0:3::4713:93e3]:80" )
(let I (ipAddress (chop A))
(tab (-29 34 40 7)
A
(hex (car I))
(format (car I))
(cdr I) ) ) )

Output:

127.0.0.1                                              7F000001                              2130706433
127.0.0.1:80                                           7F000001                              2130706433     80
::1                                                           1                                       1
[::1]:80                                                      1                                       1     80
2605:2700:0:3::4713:93e3       260527000000000300000000471393E3  50537416338094019778974086937420469219
[2605:2700:0:3::4713:93e3]:80  260527000000000300000000471393E3  50537416338094019778974086937420469219     80

[edit] PL/I

*process or(!) source xref attributes macro options;
/*********************************************************************
* Program to parse an IP address into --> IPv4 or IPv6 format
* 28.05.2013 Walter Pachl translated from REXX version 3
* x2d was the hard part :-)
*********************************************************************/

ip: proc options(main);
Dcl ipa char(50) Var;
Dcl ipi char(50) Var;
Dcl ipax char(50) Var Init('');
Dcl ipad char(50) Var Init('');
Dcl space char(4);
Dcl port char(5) Var;
dcl head Char(132) Var;
head=' input IP address hex IP address '!!
' decimal IP address space port';
Put Edit(head)(Skip,a);
Put Edit(copies('_',30),
copies('_',32),
copies('_',39),
copies('_', 5),
copies('_', 5))
(Skip,6(a,x(1)));
 
call expand('127.0.0.1');
call expand('127.0.0.1:80');
call expand('2605:2700:0:3::4713:93e3');
call expand('[2605:2700:0:3::4713:93e3]:80');
call expand('::1');
call expand('[::1]:80');
 
expand: procedure(s);
Dcl s Char(50) Var;
ipi=s;
ipa=s;
If index(ipa,'.')>0 Then
Call expand_ipv4;
Else
Call expand_ipv6;
ipad=x2d(ipax);
Put Edit(left(ipi,30),right(ipax,32),right(ipad,39),
right(space,5),right(port,5))
(Skip,6(a,x(1)));
End;
 
expand_ipv4: Proc;
Dcl a(4) Char(3) Var;
Dcl (pp,j) Bin Fixed(31);
space='IPv4';
pp=index(ipa,':');
If pp>0 Then Do;
port=substr(ipa,pp+1);
ipa=left(ipa,pp-1);
End;
Else
Port='';
Call parse((ipa),'.',a);
ipax='';
do j=1 To 4;
ipax=ipax!!a(j);
end;
End;
 
expand_ipv6: Proc;
Dcl a(8) Char(4) Var;
Dcl (s,o1,o2) Char(50) Var Init('');
Dcl (i,ii,pp,j,n) Bin Fixed(31) Init(0);
space='IPv6';
pp=index(ipa,']:');
If pp>0 Then Do;
port=substr(ipa,pp+2);
ipa=substr(ipa,2,pp-2);
End;
Else
Port='';
s=ipa;
j=0;
Do i=1 To 8 While(s>'');
pp=index(s,':');
dcl temp Char(6) Var;
If pp>1 Then
temp=left(s,pp-1);
Else
temp=s;
temp=right(temp,4,'0');
Select(pp);
When(0) Do;
a(i)=temp;
s='';
End;
When(1) Do;
a(i)='----';
ii=i;
s=substr(s,pp+1);
If left(s,1)=':' Then
s=substr(s,2);
End;
Otherwise Do;
a(i)=temp;
s=substr(s,pp+1);
End;
End;
End;
n=i-1;
o1='';
o2='';
Do i=1 To n;
If i=ii Then Do;
o1=o1!!'----';
Do j=1 To 9-n;
o2=o2!!'0000';
End;
End;
Else Do;
o1=o1!!right(a(i),4,'0');
o2=o2!!right(a(i),4,'0');
End;
End;
ipax=o2;
End;
 
parse: Proc(s,c,a);
Dcl s Char(50) Var;
Dcl c Char( 1);
Dcl a(*) Char(*) Var;
Dcl (i,p) Bin Fixed(31);
a='';
Do i=1 By 1 While(length(s)>0);
p=index(s,c);
If p>0 Then Do;
a(i)=left(s,p-1);
s=substr(s,p+1);
End;
Else Do;
a(i)=s;
s='';
End;
End;
End;
 
/*
underscore: Proc(s) Returns(char(132) Var);
Dcl s Char(*);
Dcl r Char(length(s)) Var Init('');
Dcl i Bin Fixed(31);
Dcl us Bit(1) Init('0'b);
Do i=1 To length(s)-1;
If substr(s,i,1)>' ' Then Do;
r=r!!'_';
us='1'b;
End;
Else Do;
If substr(s,i+1,1)>' ' & us Then
r=r!!'_';
Else Do;
r=r!!' ';
us='0'b;
End;
End;
End;
If substr(s,length(s),1)>' ' Then
r=r!!'_';
Return(r);
End;
 
center: Proc(s,l) Returns(char(50) Var);
Dcl s char(50) Var;
Dcl (l,b) Bin Fixed(31);
b=(l-length(s))/2;
Return(left(copies(' ',b)!!s,l));
End;
*/

copies: Proc(c,n) Returns(char(50) Var);
Dcl c char(50) Var;
Dcl n Bin Fixed(31);
Return(repeat(c,n-1));
End;
 
 
c2d: Procedure(s) Returns(Char(50) Var);
Dcl s Char(*) Var;
Dcl d Pic'99';
Dcl (v,part,result,old) Char(100) Var;
Dcl i Bin Fixed(31);
result='0';
v='1';
 
Do i=length(s) To 1 By -1;
d=c2d(substr(s,i,1));
part=longmult((v),(d));
result=longadd((result),(part));
v=longmult((v),'16');
End;
Do While(left(result,1)='0');
result=substr(result,2);
End;
Return(result);
/*
dbg: Proc(txt);
Dcl txt Char(*);
Put Skip list(txt);
End;
*/

x2d: Procedure(c) Returns(Char(2));
Dcl c Char(1);
Dcl res Char(2);
Select(c);
When('a','A') res='10';
When('b','B') res='11';
When('c','C') res='12';
When('d','D') res='13';
When('e','E') res='14';
When('f','F') res='15';
Otherwise res='0'!!c;
End;
Return(res);
End;
 
longmult: Procedure(as,bs) Returns(Char(1000) Var);
/* REXX **************************************************************
* Multiply(as,bs) -> as*bs
*********************************************************************/

Dcl (as,bs) Char(*);
Dcl (a(1000),b(1000),r(1000)) Pic'9';
Dcl (p,s) Pic'99';
Dcl (al,bl) Bin Fixed(31);
Dcl (i,ai,bi,ri,rim) Bin Fixed(31);
Dcl res Char(1000) Var Init((1000)'0');
al=length(as); Do ai=al To 1 By -1; a(ai)=substr(as,al-ai+1,1); End;
bl=length(bs); Do bi=bl To 1 By -1; b(bi)=substr(bs,bl-bi+1,1); End;
r=0;
rim=0;
Do bi=1 To bl;
Do ai=1 To al;
ri=ai+bi-1;
p=a(ai)*b(bi);
Do i=ri by 1 Until(p=0);
s=r(i)+p;
r(i)=mod(s,10);
p=s/10;
End;
rim=max(rim,i);
End;
End;
res='';
Do i=1 To rim;
res=r(i)!!res;
End;
Return(res);
End;
 
longadd: proc(as,bs) Returns(Char(100) Var);
Dcl (as,bs) Char(*) Var;
Dcl cs Char(100) Var Init('');
Dcl (al,bl,cl,i) Bin Fixed(31);
Dcl a(100) Pic'9' Init((100)0);
Dcl b(100) Pic'9' Init((100)0);
Dcl c(100) Pic'9' Init((100)0);
Dcl temp Pic'99';
al=length(as);
bl=length(bs);
Do i=1 To al; a(i)=substr(as,al-i+1,1); End;
Do i=1 To bl; b(i)=substr(bs,bl-i+1,1); End;
cl=max(al,bl)+1;
Do i=1 To cl;
temp=a(i)+b(i)+c(i);
c(i)=mod(temp,10);
c(i+1)=c(i+1)+temp/10;
End;
Do i=1 To cl;
cs=c(i)!!cs;
End;
Return(cs);
End;
End;
 
end;

Output:


       input IP address                 hex IP address                    decimal IP address            space  port
______________________________ ________________________________ _______________________________________ _____ _____
127.0.0.1                                                127001                                 1208321  IPv4
127.0.0.1:80                                             127001                                 1208321  IPv4    80
2605:2700:0:3::4713:93e3       260527000000000300000000471393e3  50537416338094019778974086937420469219  IPv6
[2605:2700:0:3::4713:93e3]:80  260527000000000300000000471393e3  50537416338094019778974086937420469219  IPv6    80
::1                            00000000000000000000000000000001                                       1  IPv6
[::1]:80                       00000000000000000000000000000001                                       1  IPv6    80

[edit] Python

Library: pyparse

The following uses pyparse to parse the IP address. It's an attempt at using pyparse to describe an IP address in an extended BNF syntax. Using a parser does seems a bit like using a sledgehammer to crack a nut. However it does make for an interesting alternative to using a regular expressions to parse IP addresses. Note - for example - that the parser specifically reports - as an exception - the location where the IP address is syntactically wrong.

import string
from pyparsing import * # import antigravity
 
tests="""#
127.0.0.1 # The "localhost" IPv4 address
127.0.0.1:80 # The "localhost" IPv4 address, with a specified port (80)
::1 # The "localhost" IPv6 address
[::1]:80 # The "localhost" IPv6 address, with a specified port (80)
2605:2700:0:3::4713:93e3 # Rosetta Code's primary server's public IPv6 address
[2605:2700:0:3::4713:93e3]:80 # Rosetta Code's primary server's public IPv6 address, +port (80)
2001:db8:85a3:0:0:8a2e:370:7334 # doc, IPv6 for 555-1234
2001:db8:85a3::8a2e:370:7334 # doc
[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443 # doc +port
192.168.0.1 # private
::ffff:192.168.0.1 # private transitional
::ffff:71.19.147.227 # Rosetta Code's transitional
[::ffff:71.19.147.227]:80 # Rosetta Code's transitional +port
:: # unspecified
256.0.0.0 # invalid, octet > 255 (currently not detected)
g::1 # invalid
0000 Bad address
0000:0000 Bad address
0000:0000:0000:0000:0000:0000:0000:0000 Good address
0000:0000:0000::0000:0000 Good Address
0000::0000::0000:0000 Bad address
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff Good address
ffff:ffff:ffff:fffg:ffff:ffff:ffff:ffff Bad address
fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff Good address
fff:ffff:0:ffff:ffff:ffff:ffff:ffff Good address
"""

 
def print_args(args):
print "print_args:", args
 
def join(args):
args[0]="".join(args)
del args[1:]
 
def replace(val):
def lambda_replace(args):
args[0]=val
del args[1:]
return lambda_replace
 
def atoi(args): args[0]=string.atoi(args[0])
def itohex2(args): args[0]="%02x"%args[0]
 
def hextoi(args): args[0]=string.atoi(args[0], 16)
def itohex4(args): args[0]="%04x"%args[0]
 
def assert_in_range(lwb, upb):
def range_check(args):
return # turn range checking off
if args[0] < lwb:
raise ValueError,"value %d < %d"%(args[0], lwb)
if args[0] > upb:
raise ValueError,"value %d > %d"%(args[0], upb)
return range_check
 
dot = Literal(".").suppress()("dot"); colon = Literal(":").suppress()("colon")
octet = Word(nums).setParseAction(atoi,assert_in_range(0,255),itohex2)("octet");
 
port = Word(nums).setParseAction(atoi,assert_in_range(0,256*256-1))("port")
ipv4 = (octet + (dot+octet)*3)("addr")
ipv4.setParseAction(join) #,hextoi)
 
ipv4_port = ipv4+colon.suppress()+port
 
a2f = "abcdef"
hex = oneOf(" ".join(nums+a2f));
 
hexet = (hex*(0,4))("hexet")
hexet.setParseAction(join, hextoi, itohex4)
 
max=8; stop=max+1
 
xXXXX_etc = [None, hexet]; xXXXX_etc.extend([hexet + (colon+hexet)*n for n in range(1,max)])
x0000_etc = [ Literal("::").setParseAction(replace("0000"*num_x0000s)) for num_x0000s in range(stop) ]
 
ipv6=xXXXX_etc[-1]+x0000_etc[0] | xXXXX_etc[-1]
 
# Build a table of rules for IPv6, in particular the double colon
for num_prefix in range(max-1, -1, -1):
for num_x0000s in range(0,stop-num_prefix):
x0000 = x0000_etc[num_x0000s]
num_suffix=max-num_prefix-num_x0000s
if num_prefix:
if num_suffix: pat = xXXXX_etc[num_prefix]+x0000+xXXXX_etc[num_suffix]
else: pat = xXXXX_etc[num_prefix]+x0000
elif num_suffix: pat = x0000+xXXXX_etc[num_suffix]
else: pat=x0000
ipv6 = ipv6 | pat
 
ipv6.setParseAction(join) # ,hextoi)
ipv6_port = Literal("[").suppress() + ipv6 + Literal("]").suppress()+colon+port
 
ipv6_transitional = (Literal("::ffff:").setParseAction(replace("0"*20+"ffff"))+ipv4).setParseAction(join)
ipv6_transitional_port = Literal("[").suppress() + ipv6_transitional + Literal("]").suppress()+colon+port
 
ip_fmt = (
(ipv4_port|ipv4)("ipv4") |
(ipv6_transitional_port|ipv6_transitional|ipv6_port|ipv6)("ipv6")
) + LineEnd()
 
class IPAddr(object):
def __init__(self, string):
self.service = dict(zip(("address","port"), ip_fmt.parseString(string)[:]))
def __getitem__(self, key): return self.service[key]
def __contains__(self, key): return key in self.service
def __repr__(self): return `self.service` # "".join(self.service)
address=property(lambda self: self.service["address"])
port=property(lambda self: self.service["port"])
is_service=property(lambda self: "port" in self.service)
version=property(lambda self: {False:4, True:6}[len(self.address)>8])
 
for test in tests.splitlines():
if not test.startswith("#"):
ip_str, desc = test.split(None,1)
print ip_str,"=>",
try:
ip=IPAddr(ip_str)
print ip, "IP Version:",ip.version,"- Address is OK!",
except (ParseException,ValueError), details: print "Bad! IP address syntax error detected:",details,
print "- Actually:",desc

Output:

127.0.0.1 => {'address': '7f000001'} IP Version: 4 - Address is OK! - Actually: # The "localhost" IPv4 address
127.0.0.1:80 => {'port': 80, 'address': '7f000001'} IP Version: 4 - Address is OK! - Actually: # The "localhost" IPv4 address, with a specifie
d port (80)
::1 => {'address': '00000000000000000000000000000001'} IP Version: 6 - Address is OK! - Actually: # The "localhost" IPv6 address
[::1]:80 => {'port': 80, 'address': '00000000000000000000000000000001'} IP Version: 6 - Address is OK! - Actually: # The "localhost" IPv6 addr
ess, with a specified port (80)
2605:2700:0:3::4713:93e3 => {'address': '260527000000000300000000471393e3'} IP Version: 6 - Address is OK! - Actually: # Rosetta Code's primar
y server's public IPv6 address
[2605:2700:0:3::4713:93e3]:80 => {'port': 80, 'address': '260527000000000300000000471393e3'} IP Version: 6 - Address is OK! - Actually: # Rose
tta Code's primary server's public IPv6 address, +port (80)
2001:db8:85a3:0:0:8a2e:370:7334 => {'address': '20010db885a3000000008a2e03707334'} IP Version: 6 - Address is OK! - Actually: # doc, IPv6 for 
555-1234
2001:db8:85a3::8a2e:370:7334 => {'address': '20010db885a3000000008a2e03707334'} IP Version: 6 - Address is OK! - Actually: # doc
[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443 => {'port': 443, 'address': '20010db885a308d313198a2e03707348'} IP Version: 6 - Address is OK! - Ac
tually: # doc +port
192.168.0.1 => {'address': 'c0a80001'} IP Version: 4 - Address is OK! - Actually: # private
::ffff:192.168.0.1 => {'address': '00000000000000000000ffffc0a80001'} IP Version: 6 - Address is OK! - Actually: # private transitional
::ffff:71.19.147.227 => {'address': '00000000000000000000ffff471393e3'} IP Version: 6 - Address is OK! - Actually: # Rosetta Code's transition
al
[::ffff:71.19.147.227]:80 => {'port': 80, 'address': '00000000000000000000ffff471393e3'} IP Version: 6 - Address is OK! - Actually: # Rosetta 
Code's transitional +port
:: => {'address': '00000000000000000000000000000000'} IP Version: 6 - Address is OK! - Actually: # unspecified
256.0.0.0 => {'address': '100000000'} IP Version: 6 - Address is OK! - Actually: # invalid, octet > 255 (currently not detected)
g::1 => Bad! IP address syntax error detected:  (at char 4), (line:1, col:5) - Actually: # invalid
0000 => Bad! IP address syntax error detected: Expected "." (at char 4), (line:1, col:5) - Actually: Bad address
0000:0000 => Bad! IP address syntax error detected: Expected ":" (at char 9), (line:1, col:10) - Actually: Bad address
0000:0000:0000:0000:0000:0000:0000:0000 => {'address': '00000000000000000000000000000000'} IP Version: 6 - Address is OK! - Actually: Good add
ress
0000:0000:0000::0000:0000 => {'address': '00000000000000000000000000000000'} IP Version: 6 - Address is OK! - Actually: Good Address
0000::0000::0000:0000 => Bad! IP address syntax error detected: Expected end of line (at char 10), (line:1, col:11) - Actually: Bad address
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff => {'address': 'ffffffffffffffffffffffffffffffff'} IP Version: 6 - Address is OK! - Actually: Good add
ress
ffff:ffff:ffff:fffg:ffff:ffff:ffff:ffff => Bad! IP address syntax error detected: Expected ":" (at char 18), (line:1, col:19) - Actually: Bad 
address
fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff => {'address': '0fffffffffffffffffffffffffffffff'} IP Version: 6 - Address is OK! - Actually: Good addr
ess
fff:ffff:0:ffff:ffff:ffff:ffff:ffff => {'address': '0fffffff0000ffffffffffffffffffff'} IP Version: 6 - Address is OK! - Actually: Good address

[edit] Racket

 
#lang racket
 
(require net/private/ip)
 
(define (bytes->hex bs)
(string-append* (map (λ(n) (~r n #:base 16 #:min-width 2 #:pad-string "0"))
(bytes->list bs))))
 
(define (parse-ip str)
(define-values [ipstr portstr]
(match str
[(regexp #rx"^([0-9.]+):([0-9]+)$" (list _ i p)) (values i p)]
[(regexp #rx"^\\[([0-9a-fA-F:]+)\\]:([0-9]+)$" (list _ i p)) (values i p)]
[_ (values str "")]))
(define ip (make-ip-address ipstr))
(define 4? (ipv4? ip))
(define hex (bytes->hex ((if 4? ipv4-bytes ipv6-bytes) ip)))
(displayln (~a (~a str #:min-width 30)
" "
(~a hex #:min-width 32 #:align 'right)
" ipv" (if 4? "4" "6") " " portstr)))
 
(for-each parse-ip
'("127.0.0.1"
"127.0.0.1:80"
"::1"
"[::1]:80"
"2605:2700:0:3::4713:93e3"
"[2605:2700:0:3::4713:93e3]:80"))
 
Output:
127.0.0.1                                              7f000001 ipv4 
127.0.0.1:80                                           7f000001 ipv4 80
::1                            00000000000000000000000000000001 ipv6 
[::1]:80                       00000000000000000000000000000001 ipv6 80
2605:2700:0:3::4713:93e3       260527000000000300000000471393e3 ipv6 
[2605:2700:0:3::4713:93e3]:80  260527000000000300000000471393e3 ipv6 80

[edit] REXX

One of REXX's strongest features is its ability for parsing, it has PARSE instruction for this purpose.

[edit] version 1

/*REXX program to parse an IP address into ──► IPv4 or IPv6 format, port*/
say center('input IP address' ,30),
center('hex IP address' ,32),
center('decimal IP address' ,39) 'space port'
_='─'
say copies(_,30) copies(_,32) copies(_,39) copies(_,5) copies(_,5)
call IP_parse 127.0.0.1 /*this IP doesn't need quotes. */
call IP_parse '127.0.0.1:80'
call IP_parse '::1'
call IP_parse '[::1]:80'
call IP_parse '2605:2700:0:3::4713:93e3'
call IP_parse '[2605:2700:0:3::4713:93e3]:80'
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────IP_parse subroutine─────────────────*/
IP_parse: procedure; parse arg a .; hx=; @.=; numeric digits 50
dot=pos('.',a)\==0 /*see if there is a dot present. */
 
if dot then do; parse var a @.1 '.' @.2 "." @.3 '.' @.4 ":" port
do j=1 for 4; hx=hx || d2x(@.j,2)
end /*j*/
end /*if dot*/
else do; parse var a pureA ']:' port
_=space(translate(pureA,,'[]'),0) /*remove brackets*/
parse var _ x '::' y
do L=1 until x=='' /*get left side.*/
parse var x @.L ':' x
end /*L*/
y=reverse(y)
do r=8 by -1 /*get right side.*/
parse var y z ':' y; if z=='' then leave
@.r=reverse(z)
end /*r*/
 
do k=1 for 8; hx=hx || right(word(@.k 0, 1), 4, 0)
end /*k*/
end /*else dot*/
 
say left(a,30) right(hx,32) right(x2d(hx),39) ' IPv'||(6-2*dot) right(port,5)
return

output using the default input

       input IP address                 hex IP address                    decimal IP address            space  port
────────────────────────────── ──────────────────────────────── ─────────────────────────────────────── ───── ─────
127.0.0.1                                              7F000001                              2130706433  IPv4
127.0.0.1:80                                           7F000001                              2130706433  IPv4    80
::1                            00000000000000000000000000000001                                       1  IPv6
[::1]:80                       00000000000000000000000000000001                                       1  IPv6    80
2605:2700:0:3::4713:93e3       260527000000000300000000471393e3  50537416338094019778974086937420469219  IPv6
[2605:2700:0:3::4713:93e3]:80  260527000000000300000000471393e3  50537416338094019778974086937420469219  IPv6    80

[edit] version 2

/* REXX ***************************************************************
* 27.05.2013 Walter Pachl
**********************************************************************/

Numeric Digits 100
say center('input IP address' ,30),
center('hex IP address' ,32),
center('decimal IP address' ,39) 'space port'
 
say copies(_,30) copies(_,32) copies(_,39) copies(_,5) copies(_,5) /*hdr*/
call expand '127.0.0.1'
call expand '127.0.0.1:80'
call expand '::1'
call expand '[::1]:80'
call expand '2605:2700:0:3::4713:93e3'
call expand '[2605:2700:0:3::4713:93e3]:80'
Say ' '
Say 'The following 2 are the same'
Call expand '2001:0db8:0:0:0:0:1428:57ab'
Call expand '2001:db8::1428:57ab'
Say ' '
Say 'The following 3 are all the same'
Call expand '2001:0db8:0:0:8d3:0:0:0'
Call expand '2001:db8:0:0:8d3::'
Call expand '2001:db8::8d3:0:0:0'
Exit
 
expand: Procedure
Parse Arg s
If pos('.',s)>0 Then
Parse Value expand_ip4(s) With exp space port
Else
Parse Value expand_ip6(s) With exp space port
Say left(s,30) right(exp,32) right(x2d(exp),39) right(space,5) right(port,5)
Return
 
expand_ip4: Procedure
Parse Arg s
If pos(':',s)>0 Then
Parse Var s s ':' port
Else
port=''
Do i=1 To 4
Parse Var s a.i '.' s
End
res=''
Do i=1 To 4
res=res||d2x(a.i,2)
End
Return res 'IPv4' port
 
expand_ip6: Procedure
/**********************************************************************
* Note: Doublecolon ('::') requires the inclusion of as many 0000
* tokens as necessary to result in 8 tokens
**********************************************************************/

Parse Arg s
If pos(']:',s)>0 Then
Parse Var s '[' s ']:' port
Else
port=''
sc=s
ii=0
Do i=1 To 8 While s<>''
Parse Var s x.i ':' s
If left(s,1)=':' Then Do
ii=i
s=substr(s,2)
End
End
n=i-1
ol=''
o2=''
Do i=1 To n
ol=ol||right(x.i,4,'0')
o2=o2||right(x.i,4,'0')
If i=ii Then Do
ol=ol||'----'
Do j=1 To 8-n
o2=o2||'0000'
End
End
End
Return o2 'IPv6' port

Output:


       input IP address                 hex IP address                    decimal IP address            space  port
______________________________ ________________________________ _______________________________________ _____ _____
127.0.0.1                                              7F000001                              2130706433  IPv4
127.0.0.1:80                                           7F000001                              2130706433  IPv4    80
::1                            00000000000000000000000000000001                                       1  IPv6
[::1]:80                       00000000000000000000000000000001                                       1  IPv6    80
2605:2700:0:3::4713:93e3       260527000000000300000000471393e3  50537416338094019778974086937420469219  IPv6
[2605:2700:0:3::4713:93e3]:80  260527000000000300000000471393e3  50537416338094019778974086937420469219  IPv6    80

The following 2 are the same
2001:0db8:0:0:0:0:1428:57ab    20010db80000000000000000142857ab  42540766411282592856903984951992014763  IPv6
2001:db8::1428:57ab            20010db80000000000000000142857ab  42540766411282592856903984951992014763  IPv6

The following 3 are all the same
2001:0db8:0:0:8d3:0:0:0        20010db80000000008d3000000000000  42540766411282592857539836924043198464  IPv6
2001:db8:0:0:8d3::             20010db80000000008d3000000000000  42540766411282592857539836924043198464  IPv6
2001:db8::8d3:0:0:0            20010db80000000008d3000000000000  42540766411282592857539836924043198464  IPv6

[edit] Ruby

Ruby 1.9.2 has better IPv6 support than older versions. This script uses class Addrinfo from Ruby 1.9.2.

Works with: Ruby version 1.9.2
require 'socket'
require 'ipaddr'
 
IP_ADDRESSES = ["127.0.0.1", "127.0.0.1:80",
"::1", "[::1]:80",
"2605:2700:0:3::4713:93e3", "[2605:2700:0:3::4713:93e3]:80",
"fe80::1%lo0", "1600 Pennsylvania Avenue NW"]
output = []
output << %w(String Address Port Family Hex Scope?)
output << %w(------ ------- ---- ------ --- ------)
 
# Parse _string_ for an IP address and optional port number. Returns
# them in an Addrinfo object.
def parse_addr(string)
# Split host and port number from string.
case string
when /\A\[(?<address> .* )\]:(?<port> \d+ )\z/x # string like "[::1]:80"
address, port = $~[:address], $~[:port]
when /\A(?<address> [^:]+ ):(?<port> \d+ )\z/x # string like "127.0.0.1:80"
address, port = $~[:address], $~[:port]
else # string with no port number
address, port = string, nil
end
 
# Pass address, port to Addrinfo.getaddrinfo. It will raise SocketError if address or port is not valid.
# IPAddr currently cannot handle ::1 notation, use Addrinfo instead
ary = Addrinfo.getaddrinfo(address, port)
 
# An IP address is exactly one address.
ary.size == 1 or raise SocketError, "expected 1 address, found #{ary.size}"
ary.first
end
 
def output_table(rows)
widths = []
rows.each {|row| row.each_with_index {|col, i| widths[i] = [widths[i].to_i, col.to_s.length].max }}
format = widths.map {|size| "%#{size}s"}.join("\t")
rows.each {|row| puts format % row}
end
 
family_hash = {Socket::AF_INET => "ipv4", Socket::AF_INET6 => "ipv6"}
 
IP_ADDRESSES.each do |string|
begin
addr = parse_addr(string)
rescue SocketError
output << [string, "illegal address", '','','','']
else
(cur_string ||= []) << string << addr.ip_address << addr.ip_port.to_s << family_hash[addr.afamily] # for output
 
# Show address in hexadecimal. We must unpack it from sockaddr string.
if addr.ipv4?
# network byte order "N"
cur_string << "0x%08x" % IPAddr.new(addr.ip_address).hton.unpack('N') << ""
elsif addr.ipv6?
# 32 bytes for address, network byte order "N4"
cur_string << "0x%032x" % IPAddr.new(addr.ip_address).to_i
cur_string << (addr.ipv6_linklocal? ? ary[4] : "") # for Scope
end
output << cur_string
end
end
 
output_table output
 
                       String                    Address        Port    Family                                 Hex      Scope?
                       ------                    -------        ----    ------                                 ---      ------
                    127.0.0.1                  127.0.0.1           0      ipv4                          0x7f000001
                 127.0.0.1:80                  127.0.0.1          80      ipv4                          0x7f000001
                          ::1                        ::1           0      ipv6  0x00000000000000000000000000000001
                     [::1]:80                        ::1          80      ipv6  0x00000000000000000000000000000001
     2605:2700:0:3::4713:93e3   2605:2700:0:3::4713:93e3           0      ipv6  0x260527000000000300000000471393e3
[2605:2700:0:3::4713:93e3]:80   2605:2700:0:3::4713:93e3          80      ipv6  0x260527000000000300000000471393e3
                  fe80::1%lo0            illegal address
  1600 Pennsylvania Avenue NW            illegal address

With BSD, fe80::1%lo0 is a link-local address. With other systems, fe80::1%lo0 might be illegal address, because lo0 is not valid scope identifier.

[edit] Tcl

Library: Tcllib (Package: ip)
package require Tcl 8.5
package require ip
 
proc parseIP {address} {
set result {}
set family [ip::version $address]
set port -1
if {$family == -1} {
if {[regexp {^\[(.*)\]:(\d+)$} $address -> address port]} {
dict set result port $port
set family [ip::version $address]
if {$family != 6} {
return -code error "bad address"
}
} elseif {[regexp {^(.*):(\d+)$} $address -> address port]} {
dict set result port $port
set family [ip::version $address]
if {$family != 4} {
return -code error "bad address"
}
} else {
return -code error "bad address"
}
}
# Only possible error in ports is to be too large an integer
if {$port > 65535} {
return -code error "bad port"
}
dict set result family $family
if {$family == 4} {
# IPv4 normalized form is dotted quad, but toInteger helps
dict set result addr [format %x [ip::toInteger $address]]
} else {
# IPv6 normalized form is colin-separated hex
dict set result addr [string map {: ""} [ip::normalize $address]]
}
# Return the descriptor dictionary
return $result
}

Demonstration code:

foreach address {
127.0.0.1
127.0.0.1:80
 ::1
[::1]:80
2605:2700:0:3::4713:93e3
[2605:2700:0:3::4713:93e3]:80
 ::ffff:192.168.0.1
[::ffff:192.168.0.1]:22
 ::ffff:127.0.0.0.1
a::b::1
127.0.0.1:100000
} {
if {[catch {
set parsed [parseIP $address]
} msg]} {
puts "error ${msg}: \"$address\""
continue
}
dict with parsed {
puts -nonewline "family: IPv$family addr: $addr"
if {[dict exists $parsed port]} {
puts -nonewline " port: $port"
}
puts ""
}
}

Output:

family: IPv4 addr: 7f000001
family: IPv4 addr: 7f000001 port: 80
family: IPv6 addr: 00000000000000000000000000000001
family: IPv6 addr: 00000000000000000000000000000001 port: 80
family: IPv6 addr: 260527000000000300000000471393e3
family: IPv6 addr: 260527000000000300000000471393e3 port: 80
family: IPv6 addr: 00000000000000000000ffffc0a80001
family: IPv6 addr: 00000000000000000000ffffc0a80001 port: 22
error bad address: "::ffff:127.0.0.0.1"
error bad address: "a::b::1"
error bad port: "127.0.0.1:100000"
Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox