Getting first address of specified IP and prefixlen in PHP

Dumping this here so I can refer to this when needed and in case anyone is looking for this.

function inet_prefixlen_start(string $inet, int $version, int $prefixlen): ?string
{
    switch ($version) {
        case 4:
            if (filter_var($inet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
                $pack = 'c*';
                $size = 8;
            }
            break;
        case 6:
            if (filter_var($inet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
                $pack = 'n*';
                $size = 16;
            }
            break;
    }

    if (!isset($pack)) {
        return null;
    }

    $ipArray = unpack($pack, inet_pton($inet));
    $groupMask = (1 << $size) - 1;
    $fullSize = $size * count($ipArray);
    $groupCount = count($ipArray);
    for ($i = 0; $i < $groupCount; $i++) {
        $mask = $groupMask;
        $indexStart = $i * $size;
        for ($j = 0; $j < $size; $j++) {
            $fullIndex = $indexStart + $j;
            if ($fullIndex < $prefixlen) {
                continue;
            }
            // shifting goes from the end, one less of size:
            // (size 8)
            // i = 0 -> shift = 7 (1000 0000)
            // i = 7 -> shift = 0 (0000 0001)
            $index = $size - $j - 1;
            $mask ^= 1 << $index;
        }
        // ipArray is 1-indexed because that's how unpack works
        $group = $i + 1;
        $ipArray[$group] = $ipArray[$group] & $mask;
    }

    return inet_ntop(pack($pack, ...$ipArray));
}

/**
some sample data
['1.2.3.4', 4, 24, '1.2.3.0'],
['1.2.3.5', 4, 31, '1.2.3.4'],
['::10.0.0.3', 4, 32, null],
['1:2:3:4::', 4, 24, null],
['invalid', 4, 32, null],
['1:2:3:4::1', 6, 64, '1:2:3:4::'],
['1:2:3::1', 6, 64, '1:2:3::'],
['1:2:3:4:5:6:7:8', 6, 64, '1:2:3:4::'],
['1::4:5:6:7:8', 6, 64, '1:0:0:4::'],
['1:2:3:4::10.0.0.1', 6, 64, '1:2:3:4::'],
['1:2:3:ffff::1', 6, 56, '1:2:3:ff00::'],
['::3', 6, 127, '::2'],
['1.2.3.4', 6, 128, null],
['invalid', 6, 128, null],
*/

Or just use one of those IP handling libraries.

dirlist-php

A few years ago I wrote a php script to provide better autoindex within nginx. I used it for quite a long period until I rewrote it in Ruby/Sinatra. But then I figured the setup for it is overly complicated just for simple task. And takes additional memory. I always have php-fpm running anyway so it’s free.

And so I decided to take up the old php script and fix it up. Unfortunately there isn’t other language as easy to setup for web as php which is why I fixed it instead of rewriting in some other languages (or keeping it ruby). The “fixed” version is still pretty much imperative-style but hey, it works.

Only tested with nginx.

OpenBSD 5.0

This blog is now running on OpenBSD 5.0. Too bad php-fpm didn’t get to 5.0.

OpenBSD tomoka.myconan.net 5.0 GENERIC.MP#59 i386

The upgrade process went without any problems. Upgrading packages also went relatively well apart of php being a failure because of change to infrastructure (which allowed multiple versions to be installed). Otherwise everything upgraded without hitch and finished quickly. Sure is nice depart from FreeBSD’s ports which takes hours to update a package (upgrading system is relatively quick though using freebsd-update).

Unless there’s critical security vulnerability or something happened to the datacenter, I expect there will be no reboot until next upgrade (6 month uptime). We will see.

nginx links

Here are the links I find useful:

* [Why nginx](http://hostingfu.com/article/nginx-vs-lighttpd-for-a-small-vps)
* [WordPress with nginx](http://elasticdog.com/2008/02/howto-install-wordpress-on-nginx/)

Note that the latter link should not be followed anymore.

_Last update 2011-07-10 20:24: markdown-fied_