Canonicalize CIDR: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|REXX}}: added the computer programming language REXX.)
m (→‎{{header|REXX}}: fixed the default IPv4 address.)
Line 303: Line 303:
<lang rexx>/*REXX pgm canonicalizes IPv4 addresses that are in CIDR notation (dotted─dec/network).*/
<lang rexx>/*REXX pgm canonicalizes IPv4 addresses that are in CIDR notation (dotted─dec/network).*/
parse arg z . /*obtain optional argument from the CL.*/
parse arg z . /*obtain optional argument from the CL.*/
if z=='' | z=="," then z= '87.70.141.122/22' /*Not specified? Then use the default.*/
if z=='' | z=="," then z= '87.70.141.1/22' /*Not specified? Then use the default.*/
parse var z # '/' -0 mask /*get the address nodes & optional mask*/
parse var z # '/' -0 mask /*get the address nodes & optional mask*/
#= translate(#, , .) /*elide periods from IPv4 address nodes*/
#= translate(#, , .) /*elide periods from IPv4 address nodes*/
$= /*initialize the to-be reassembled IPv4*/
$= /*initialize the to-be reassembled IPv4*/
do j=1 for words(#); x= word(#, j) /*process each of the address nodes. */
do j=1 for words(#); x= word(#, j) /*process each of the address nodes. */
if x//2 then x= x - 1 /*Is low─order bit on? Then remove it.*/
if j>1 & x//2 then x= x - 1 /*Is low─order bit on? Then remove it.*/
$= $ x /*reconstruct (by abutment) IPv4 nodes.*/
$= $ x /*reconstruct (by abutment) IPv4 nodes.*/
end /*j*/ /*(REXX stores it's numbers in decimal)*/
end /*j*/ /*(REXX stores it's numbers in decimal)*/
Line 316: Line 316:
{{out|output|text=&nbsp; when using the default input:}}
{{out|output|text=&nbsp; when using the default input:}}
<pre>
<pre>
original IPv4 address: 87.70.141.122/22
original IPv4 address: 87.70.141.1/22
canonicalized address: 86.70.140.122/22
canonicalized address: 87.70.140.0/22
</pre>
</pre>



Revision as of 18:16, 9 July 2020

Task
Canonicalize CIDR
You are encouraged to solve this task according to the task description, using any language you may know.

Implement a function or program that, given a range of IPv4 addresses in CIDR notation (dotted-decimal/network-bits), will return/output the same range in canonical form. That is, the IP address portion of the output CIDR block must not contain any set (1) bits in the host part of the address.

Example: given 87.70.141.1/22, your code should output 87.70.140.0/22.

C

This solution uses only the standard library. On POSIX platforms one can use the functions inet_pton/inet_ntop to parse/format IPv4 addresses. <lang c>#include <stdbool.h>

  1. include <stdio.h>
  2. include <stdint.h>

typedef struct cidr_tag {

   uint32_t address;
   uint32_t mask;

} cidr_t;

// Convert a string in CIDR format to an IPv4 address and netmask, // if possible. Also performs CIDR canonicalization. bool cidr_parse(const char* str, cidr_t* cidr) {

   int a, b, c, d, m;
   if (sscanf(str, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m) != 5)
       return false;
   if (m < 1 || m > 32)
       return false;
   if (a < 0 || a > UINT8_MAX)
       return false;
   if (b < 0 || b > UINT8_MAX)
       return false;
   if (c < 0 || c > UINT8_MAX)
       return false;
   if (d < 0 || d > UINT8_MAX)
       return false;
   uint32_t mask = ~((1 << (32 - m)) - 1);
   uint32_t address = (a << 24) + (b << 16) + (c << 8) + d;
   address &= mask;
   cidr->address = address;
   cidr->mask = mask;
   return true;

}

uint8_t mask_length(uint32_t mask) {

   uint8_t length = 32;
   for (; (mask & 1) == 0 && length > 0; mask >>= 1, --length) {}
   return length;

}

// Write a string in CIDR notation into the supplied buffer. void cidr_format(const cidr_t* cidr, char* str, size_t size) {

   uint8_t m = mask_length(cidr->mask);
   uint32_t address = cidr->address;
   uint8_t d = address & UINT8_MAX;
   address >>= 8;
   uint8_t c = address & UINT8_MAX;
   address >>= 8;
   uint8_t b = address & UINT8_MAX;
   address >>= 8;
   uint8_t a = address & UINT8_MAX;
   snprintf(str, size, "%hhu.%hhu.%hhu.%hhu/%hhu", a, b, c, d, m);

}

int main(int argc, char** argv) {

   for (int i = 1; i < argc; ++i) {
       cidr_t cidr;
       if (cidr_parse(argv[i], &cidr)) {
           char out[32];
           cidr_format(&cidr, out, sizeof(out));
           puts(out);
       } else {
           fprintf(stderr, "%s: invalid CIDR\n", argv[i]);
       }
   }
   return 0;

}</lang>

Output:
$ ./canonicalize_cidr 87.70.141.1/22
87.70.140.0/22

Factor

Translation of: Ruby
Works with: Factor version 0.99 2020-07-03

<lang factor>USING: command-line formatting grouping io kernel math.parser namespaces prettyprint sequences splitting ; IN: rosetta-code.canonicalize-cidr

! canonicalize a CIDR block: make sure none of the host bits are set command-line get [ lines ] when-empty [

   ! ( CIDR-IP -- bits-in-network-part dotted-decimal )
   "/" split first2 string>number swap
   ! get IP as binary string
   "." split [ string>number "%08b" sprintf ] map "" join
   ! replace the host part with all zeros
   over cut length [ CHAR: 0 ] "" replicate-as append
   ! convert back to dotted-decimal
   8 group [ bin> number>string ] map "." join swap
   ! and output
   "%s/%d\n" printf

] each</lang>

Output:
$ canonicalize-cidr.factor 87.70.141.1/22
87.70.140.0/22

Go

Translation of: Ruby

<lang go>package main

import (

   "fmt"
   "log"
   "os"
   "strconv"
   "strings"

)

func check(err error) {

   if err != nil {
       log.Fatal(err) 
   }

}

func main() {

   // canonicalize a CIDR block: make sure none of the host bits are set
   var cidr string
   if len(os.Args) > 1 {
       cidr = os.Args[1]
   } else {
       log.Fatal("Please pass the CIDR to be canonicalized.")
   }
   // dotted-decimal / bits in network part
   split := strings.Split(cidr, "/")
   dotted := split[0]
   size, err := strconv.Atoi(split[1])
   check(err)
   // get IP as binary string
   var bin []string
   for _, n := range strings.Split(dotted, ".") {
       i, err := strconv.Atoi(n)
       check(err)
       bin = append(bin, fmt.Sprintf("%08b", i))
   }
   binary := strings.Join(bin, "")
   // replace the host part with all zeros
   binary = binary[0:size] + strings.Repeat("0", 32-size)
   // convert back to dotted-decimal
   var canon []string
   for i := 0; i < len(binary); i += 8 {
       num, err := strconv.ParseInt(binary[i:i+8], 2, 64)
       check(err)
       canon = append(canon, fmt.Sprintf("%d", num))
   }
   // and output
   fmt.Printf("%s/%s\n", strings.Join(canon, "."), split[1])

}</lang>

Output:
$ go run canonicalize_cidr.go 87.70.141.1/22
87.70.140.0/22

Julia

Julia has a Sockets library as a builtin, which has the types IPv4 and IPv6 for single IP addresses. <lang julia>using Sockets

function canonCIDR(cidr::String)

   cidr = replace(cidr, r"\.(\.|\/)" => s".0\1") # handle ..
   cidr = replace(cidr, r"\.(\.|\/)" => s".0\1") # handle ...
   ip = split(cidr, "/")
   dig = length(ip) > 1 ? 2^(32 - parse(UInt8, ip[2])) : 1
   ip4 = IPv4(UInt64(IPv4(ip[1])) & (0xffffffff - dig + 1))
   return length(ip) == 1 ? "$ip4" : "$ip4/$(ip[2])"

end

println(canonCIDR("87.70.141.1/22")) println(canonCIDR("100.68.0.18/18")) println(canonCIDR("10.4.30.77/30")) println(canonCIDR("110.200.21/4")) println(canonCIDR("10..55/8")) println(canonCIDR("10.../8"))

</lang>

Output:
87.70.140.0/22
100.68.0.0/18
10.4.30.76/30
96.0.0.0/4
10.0.0.0/8
10.0.0.0/8

Perl

<lang perl>#!/usr/bin/env perl use v5.16; use Socket qw(inet_aton inet_ntoa);

  1. canonicalize a CIDR block: make sure none of the host bits are set

if (!@ARGV) {

  chomp(@ARGV = <>);

}

for (@ARGV) {

 # dotted-decimal / bits in network part
 my ($dotted, $size) = split m#/#;
 # get IP as binary string
 my $binary = sprintf "%032b", unpack('N', inet_aton $dotted);
 # Replace the host part with all zeroes
 substr($binary, $size) = 0 x (32 - $size);
 # Convert back to dotted-decimal
 $dotted = inet_ntoa(pack 'B32', $binary);
 # And output
 say "$dotted/$size";

}</lang>

Output:
$ canonicalize_cidr.pl 87.70.141.1/22
87.70.140.0/22

Python

Translation of: Perl

<lang python>#!/usr/bin/env python

  1. canonicalize a CIDR block specification:
  2. make sure none of the host bits are set

import sys from socket import inet_aton, inet_ntoa from struct import pack, unpack

args = sys.argv[1:] if len(args) == 0:

   args = sys.stdin.readlines()

for cidr in args:

  # IP in dotted-decimal / bits in network part
  dotted, size_str = cidr.split('/')
  size = int(size_str)
  numeric = unpack('!I', inet_aton(dotted))[0]  # IP as an integer
  binary = f'{numeric:#034b}'                   # then as a padded binary string
  prefix = binary[:size + 2]                    # just the network part
                                                #   (34 and +2 are to account
                                                #    for leading '0b')
  canon_binary = prefix + '0' * (32 - size)     # replace host part with all zeroes
  canon_numeric = int(canon_binary, 2)          # convert back to integer
  canon_dotted = inet_ntoa(pack('!I',
                           (canon_numeric)))    # and then to dotted-decimal
  print(f'{canon_dotted}/{size}')               # output result</lang>
Output:
$ canonicalize_cidr.py 87.70.141.1/22
87.70.140.0/22

Raku

Translation of: Perl

<lang perl6>#!/usr/bin/env raku

  1. canonicalize a CIDR block: make sure none of the host bits are set

if (!@*ARGS) {

  @*ARGS = $*IN.lines;

}

for @*ARGS -> $cidr {

 # dotted-decimal / bits in network part
 my ($dotted, $size) = $cidr.split('/');
 # get IP as binary string
 my $binary = $dotted.split('.').map({ .fmt("%08b") }).join;
 # Replace the host part with all zeroes
 $binary.substr-rw($size) = 0 x (32 - $size);
 # Convert back to dotted-decimal
 my $canon = $binary.comb.batch(8).map({ .join.parse-base(2) }).join('.');
 # And output
 say "$canon/$size";

}</lang>

Output:
$ canonicalize_cidr.raku 87.70.141.1/22
87.70.140.0/22

REXX

<lang rexx>/*REXX pgm canonicalizes IPv4 addresses that are in CIDR notation (dotted─dec/network).*/ parse arg z . /*obtain optional argument from the CL.*/ if z== | z=="," then z= '87.70.141.1/22' /*Not specified? Then use the default.*/ parse var z # '/' -0 mask /*get the address nodes & optional mask*/

  1. = translate(#, , .) /*elide periods from IPv4 address nodes*/

$= /*initialize the to-be reassembled IPv4*/

    do j=1  for words(#);    x= word(#, j)      /*process each of the address nodes.   */
    if j>1  &  x//2  then x= x - 1              /*Is low─order bit on?  Then remove it.*/
    $= $ x                                      /*reconstruct (by abutment) IPv4 nodes.*/
    end   /*j*/                                 /*(REXX stores it's numbers in decimal)*/
                                                /*stick a fork in it,  we're all done. */

say ' original IPv4 address: ' z /*display the original IPv4 address. */ say ' canonicalized address: ' translate( space($), ., " ") || mask /*canonicalized.*/</lang>

output   when using the default input:
  original IPv4 address:  87.70.141.1/22
  canonicalized address:  87.70.140.0/22

Ruby

Translation of: Python
Translation of: Raku

<lang ruby>#!/usr/bin/env ruby

  1. canonicalize a CIDR block: make sure none of the host bits are set

if ARGV.length == 0 then

   ARGV = $stdin.readlines.map(&:chomp)

end

ARGV.each do |cidr|

 # dotted-decimal / bits in network part
 dotted, size_str = cidr.split('/')
 size = size_str.to_i
 # get IP as binary string
 binary = dotted.split('.').map { |o| "%08b" % o }.join
 # Replace the host part with all zeroes
 binary[size .. -1] = '0' * (32 - size)
 # Convert back to dotted-decimal
 canon = binary.chars.each_slice(8).map { |a| a.join.to_i(2) }.join('.')
 # And output
 puts "#{canon}/#{size}"

end</lang>

Output:
$ canonicalize_cidr.rb 87.70.141.1/22
87.70.140.0/22

Wren

Translation of: Ruby
Library: Wren-fmt
Library: Wren-str

<lang ecmascript>import "os" for Process import "/fmt" for Fmt, Conv import "/str" for Str

// canonicalize a CIDR block: make sure none of the host bits are set var args = Process.arguments var cidr = (args.count > 0) ? args[0] : Fiber.abort("Please pass the CIDR to be canonicalized.")

// dotted-decimal / bits in network part var split = cidr.split("/") var dotted = split[0] var size = Num.fromString(split[1])

// get IP as binary string var binary = dotted.split(".").map { |n| Fmt.swrite("$08b", Num.fromString(n)) }.join()

// replace the host part with all zeros binary = binary[0...size] + "0" * (32 - size)

// convert back to dotted-decimal var chunks = Str.chunks(binary, 8) var canon = chunks.map { |c| Conv.atoi(c, 2) }.join(".")

// and output System.print(canon + "/" + split[1])</lang>

Output:
$ wren canonicalize_cidr.wren 87.70.141.1/22
87.70.140.0/22