Converting a partial IP address string to an IP address object - powershell

Why does the System.Net.IpAddress allow the following strings to be converted to valid IP addresses?
$b = [ipaddress]"10.10.10"
$b.IPAddressToString
#10.10.0.10
$c = [ipaddress]"10.10"
$c.IPAddressToString
#10.0.0.10
$d = [ipaddress]"10"
$d.IPAddressToString
#0.0.0.10
I can see that the pattern is that the last octet in the string is the last octet in the IPAddress object, and whatever the first octets are in the string, are used as the left most octets in the IPAddress, and zeros are used to fill the middle unspecified octets, if any.
But why does it do this? As a user I'd expect it to fail during conversion unless all octets are specified.
Because it allows these conversions, unexpected results like this are possible when checking if a string is a valid IP address:
[bool]("10" -as [ipaddress]) #Outputs True

According to https://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse.aspx?f=255&MSPPError=-2147217396
The number of parts (each part is separated by a period) in ipString determines how the IP address is constructed. A one part address is stored directly in the network address. A two part address, convenient for specifying a class A address, puts the leading part in the first byte and the trailing part in the right-most three bytes of the network address. A three part address, convenient for specifying a class B address, puts the first part in the first byte, the second part in the second byte, and the final part in the right-most two bytes of the network address.

Related

Method to identify ipv4 vs ipv6 addresses

I have an ip field in a postgres database defined as an inet field.
At the moment, to determine ipv4 vs ipv6, it looks like I can use colon count and/or '.' count to some degree, but there must be a better way, right?
COLON_COUNT = (length(ip::text) - length(replace(ip::text, ':', '')));
DOT_COUNT = (length(ip::text) - length(replace(ip::text, '.', '')));
What is a clean/good way to determine if a address is ipv4 or ipv6?
Use the family() function. IPv4 will return 4, IPv6 will return 6.
I think if the trailing zeros are not ommitted you could just convert the field into a number and see if the number is less than 2 powers 32 IPv4 or not (IPV6).This means filtering out colons and dots before the number conversion.

Understanding pack / unpack perl

so I know there are libraries that can do this for me but I want to learn pack / unpack.
my goal is I have a user input an ip address / subnet mask and then verify that it's valid.
one way i thought of doing it was "sprintf" and get a binary value of lets say 192.168.1.1 . that's an ok solution, but then i need to prepend the required amount of 0's t make it '8 bit'
that seems like a lot of unnecessary work when pack can put things in binary format. I used the N template i found http://perldoc.perl.org/functions/pack.html my first goal was to get an ip address, convert it to binary, then convert it back.
$ip = "192.168.1.1";
$bi = pack ("N*", $ip);
print unpack("N*",$bi),"\n";
and the output i got was 192 so obviously i don't understand what's going on here.
what exactly is going on here?
pack ("N*", $ip) takes an integer out of $ip and puts it into network byte order.
What you want is packing the 4 decimal octets of the IP address as binary. No need to fiddle with endianness as the IP address string is already in big endian (The highest order byte is already at the start of the string).
I also changed the * to a 4, IP addresses are always 4 octets long:
$ip = "192.168.1.1";
$bi = pack "C4", split('\.', $ip);
print join('.', unpack("C4",$bi)), "\n";

Perl: Split IP address into hostid and netid

Simple enough, I'd like to split a given IP address into netid (as defined by the netmask) and the hostid in Perl. Example:
$network = NetAddr::IP->new('192.168.255.255/29') || die "invalid space $_";
Now $network->mask returns 255.255.255.248. But there're no methods in NetAddr::IP to apply the mask to split the address into its netid and hostid portions in the /29 space.
NetAddr::IP::Util mentions the operators to do so, but it's documentation is a mess.
At least the netid can be extracted using Net::NetMask:
$netid = Net::Netmask->new('192.168.255.255/29')->base;
This yields 192.168.255.248. Again, no method to get the host portion 0.0.0.7. Maybe the best would be to pack/unpack the IPs into 32 bit int and then simply & them out. Then it would be easier to print the binary representations of IP addresses too, which I found can be really helpful for debugging and documentation purposes.
Use the hostmask() method
$host_wildcard = Net::Netmask->new('192.168.255.255/29')->hostmask;

Adding special character at several places in a string in perl script

I read some raw data from my device. This data contains the IP address as well but in a different format. As you know the IP address is generally written in the format a.b.c.d. However I have data of the format abcd given from the device. I need to get this in the format a.b.c.d How do I do this in a perl script?
Regards
First, let us split the hex string into substrings of two characters:
... split /..\K/, "c0a80001";
We treat each fragment as a hex string, and get the numeric value with the hex builtin:
... map hex, ...
Then, we join all numbers with a period:
join '.', ...
Combined:
my $ip = join '.', map hex, split /..\K/, "c0a80001";
print "$ip\n";
Output: 192.168.0.1. This is the usual text representation for an IPv4 address.
There are many ways. This inserts dots with substring.
map { substr($string,$_,0)='.' } (6,4,2);
Maybe you prefer regexes.
$string =~ s/[0-9a-f]{2}\K(?!\Z)/./g;
It really depends on the approach taken, but mostly you would need to escape with a backslash the dot from the IP address
a\.b\.c\.d
Some source code with be nice btw ...

What's the simplest way of adding one to a binary string in Perl?

I have a variable that contains a 4 byte, network-order IPv4 address (this was created using pack and the integer representation). I have another variable, also a 4 byte network-order, subnet. I'm trying to add them together and add one to get the first IP in the subnet.
To get the ASCII representation, I can do inet_ntoa($ip&$netmask) to get the base address, but it's an error to do inet_ntoa((($ip&$netmask)+1); I get a message like:
Argument "\n\r&\0" isn't numeric in addition (+) at test.pm line 95.
So what's happening, the best as I can tell, is it's looking at the 4 bytes, and seeing that the 4 bytes don't represent a numeric string, and then refusing to add 1.
Another way of putting it: What I want it to do is add 1 to the least significant byte, which I know is the 4th byte? That is, I want to take the string \n\r&\0 and end up with the string \n\r&\1. What's the simplest way of doing that?
Is there a way to do this without having to unpack and re-pack the variable?
What's happening is that you make a byte string with $ip&$netmask, and then try to treat it as a number. This is not going to work, as such. What you have to feed to inet_ntoa is.
pack("N", unpack("N", $ip&$netmask) + 1)
I don't think there is a simpler way to do it.
Confusing integers and strings. Perhaps the following code will help:
use Socket;
$ip = pack("C4", 192,168,250,66); # why not inet_aton("192.168.250.66")
$netmask = pack("C4", 255,255,255,0);
$ipi = unpack("N", $ip);
$netmaski = unpack("N", $netmask);
$ip1 = pack("N", ($ipi&$netmaski)+1);
print inet_ntoa($ip1), "\n";
Which outputs:
192.168.250.1