1
0

Initial commit

This commit is contained in:
Nils Freydank 2025-02-17 13:43:03 +01:00
commit 12f27bb122
Signed by: nfr
GPG Key ID: 0F1DEAB2D36AD112
3 changed files with 104 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*~

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# compressIPv4
IPv4 addresses are typically written down as octets, e.g. `127.0.0.1`.
However the addresses seem to be actually interpreted as one large number (after conversion into binary).
This allows for funny things, like to call `http://0x7f000001:9000` to open a HTTP service on your local host, listenting on port 9000.
This repo holds a shiny tool to do the converstion for you.
It's inspired by [a blog post from 2021](https://daniel.haxx.se/blog/2021/04/19/curl-those-funny-ipv4-addresses) by the [curl CEO](https://mastodon.social/@bagder/113979166039412014).
<!--
vim:syntax=markdown:fileencoding=utf-8:ts=4:expandtab:linebreak:wrap
-->

92
compressIPv4.py Executable file
View File

@ -0,0 +1,92 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
# Nils Freydank <nils.freydan@datenschutz-ist-voll-doof.de>, 2025
import traceback
from typing import Union
__version__: str = "1.0.0"
__author__: str = "Nils Freydank <nils.freydank@datenschutz-ist-voll-doof.de>"
__copyright__: str = "MIT"
def get_compressed_v4(addr_v4: str, hex_out: bool = False) -> Union[int, str]:
"""
Read the canonical IPv4 address and compress it into a single number.
:param addr_v4: String containing the IPv4 address in dotted notation.
:param hex_out: Opt. control if the output should be hex instead of int.
:return: Integer with the compressed, decimal "IPv4 addr" - or hex string.
Return -1 or "-0x1" for invalid addresses.
Note: Function may explode in funny ways due to missed input checks.
"""
LEN_BIN_BLOCK_MAX: int = 8 # i.e. len(str(bin(255)).replace("0b", ""))
MAX_ADDR_POSSIBLE: int = 2**32 - 1 # full 32bit, i.e. 255.255.255.255
compressed_addr: str = ""
try:
blocks: list[str] = addr_v4.split(".")
bin_addr: str = ""
for block in blocks:
bin_block: str = str(bin(int(block))).replace("0b", "")
# Make sure to align each block properly to 8 bit.
len_delta: int = LEN_BIN_BLOCK_MAX - len(bin_block)
bin_addr = bin_addr + len_delta * "0" + bin_block
# Nice try. Put a valid IPv4 address inside, next time.
if (compressed_addr := int(bin_addr, 2)) > MAX_ADDR_POSSIBLE:
compressed_addr = -1
except ValueError:
print("Something broke, maybe an invalid address?")
traceback.print_exc()
return hex(compressed_addr) if hex_out else compressed_addr
if __name__ == "__main__":
# First, run some checks.
assert get_compressed_v4("0.0.0.1") == 1
assert get_compressed_v4("0.0.0.1", False) == 1
assert get_compressed_v4("0.0.0.1", True) == "0x1"
assert get_compressed_v4("0") == 0
assert get_compressed_v4("0", False) == 0
assert get_compressed_v4("0", True) == "0x0"
assert get_compressed_v4("256.0.0.0") == -1
assert get_compressed_v4("256.0.0.0", False) == -1
assert get_compressed_v4("256.0.0.0", True) == "-0x1"
assert get_compressed_v4("255.255.255.255") == 2**32 - 1
assert get_compressed_v4("255.255.255.255", False) == 2**32 - 1
assert get_compressed_v4("255.255.255.255", True) == hex(2**32 - 1)
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("ip_v4_address", help="IPv4 address that shall be compressed")
parser.add_argument(
"--hex", dest="hex", help="convert into hexadecimal", action="store_true"
)
parser.add_argument(
"--quiet",
dest="quiet",
help="be quiet do not print the greeter",
action="store_true",
)
args = parser.parse_args()
out_hex: bool = True if args.hex else False
if not args.quiet:
# Greet the user.
print("~" * 65)
print(f" v4 address fun, v{__version__}.")
print(f" by {__author__}")
print(f" use according to {__copyright__} license instructions")
print("~" * 65)
if args.ip_v4_address:
print(get_compressed_v4(args.ip_v4_address, out_hex))
# vim:syntax=python:fileencoding=utf-8:ts=4:expandtab:linebreak:wrap