#!/usr/bin/perl
$^W=1;
use strict;
use bytes;
use NetAddr::IP::Util qw(inet_aton inet_ntoa ipv6_aton ipv6_ntoa);
use Data::Validate::Domain qw(is_hostname);
use bignum;

# vi(1) :se tabstop=4

$ENV{LC_ALL}='C';

# STDIN or file argument(s) for our input data
# STDOUT is output data
# exits non-zero on errors, diagnostics to STDERR
# each line, first field is IP address
# lines are sorted in IP address network order by first field,
# IPv4, then IPv6
# if line doesn't have first field with valid IP address,
# diagnostic is written and line is otherwise skipped

my %lines=();
my $rc=0;

# Internet Protocol address to Canonical text format
# we use this so we can unambiguously compare IP addresses
my $original_line;
sub ipc{
	$#_ == 0 or die "$0: bad call to sub \&ipc(), aborting";
	my $original_ip=$_[0];
	my $ip;
	my $nip;
	$nip=inet_aton($original_ip);
	if(defined($nip)){
		$ip=inet_ntoa($nip);
	}else{
		$nip=ipv6_aton($original_ip);
		$ip=ipv6_ntoa($nip) if(defined $nip);
	};
	return($ip);
};

sub ip2b{
	# IP address to binary
	return(
		'0b'
		.
		unpack(
			'B*',
			defined(inet_aton(&ipc($_[0])))
			?
			inet_aton(&ipc($_[0]))
			:
			ipv6_aton(&ipc($_[0]))
		)
	);
};

# ==================================================================
# parse our input data,
# fix any trivial errors
# complain and exit non-zero on any non-trivial errors

while(<>)
{
	chomp;
	my $line="$_";
	my $ip;
	if(
			/\A[ \t]*([.\d:A-Fa-f)]+)(?:[ \t\r]|\z)/
			and
			defined($ip=&ipc($1))
	){
		if(exists($lines{$ip})){
			push(@{$lines{$ip}},$line);
		}else{
			$lines{$ip}=[$line];
		}
	}else{
		warn(
			"$0: skipping bad line (no IP found in first field) ",
			"on line:\n$line\n"
		);
		$rc=1;
		next;
	};
};

# Parsed and loaded our data, now just output it in dessired
# network IP address order.

#an IPv4 packed network address.
for my $ip
	(
		sort
		{
			(
				# IPv4 first:
				defined(inet_aton(&ipc($a)))
				?
					defined(inet_aton(&ipc($b)))
					?
						0
					:
						-1
				:
					defined(ipv6_aton(&ipc($b)))
					?
						0
					:
						1
			)
			||
			# both IPv4 or both IPv6, use network order:
			&ip2b($a) + 0
			<=>
			&ip2b($b) + 0
		}
		keys %lines
	)
{
	print(
		join("\n",sort @{$lines{$ip}}),
		"\n",
	);
};
exit($rc);
