Subnet validation with Zend Framework

(Note – I accidentally gave conflicting instructions to the person who runs our newsletter.  If you are actually interested in the article I wrote about people being silly about dynamicly typed languages you can go here)

I saw on a StackOverflow posting, someone was asking to see how you could use a Zend Framework validator to tell if an IP address was between two addresses.  The individual was trying to use Zend_Validate_Between to do the checking.  However, IP addresses generally are not checked between two arbitrary addresses such as between 192.168.0.45 and 192.168.0.60.  Instead, the check is usually done to validate an IP address against a subnet.

So, assuming that the individual was actually asking about subnet validation, and seeing that I couldn’t find a subnet validator for Zend Framework, I wrote a quick one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
class Esc_Validate_Subnet extends Zend_Validate_Abstract
{
    const NOT_ON_SUBNET = 'not-on-subnet';
    private $_subnet;
    private $_netmask;
    protected $_messageTemplates = array(
        self::NOT_ON_SUBNET => "'%value%' is not on the subnet"
    );
 
    public function __construct($subnet, $netmask = null)
    {
        if ($netmask === null && strpos($subnet, '/') === false) {
            throw new Zend_Validate_Exception(
                 'If the netmask is not specified then the CIDR (e.g. /24) must be provided');
        }
        if ($netmask === null) {
            $this->_subnet    = ip2long(substr($subnet, 0, strpos($subnet, '/')));
            $cidr = substr($subnet, strpos($subnet, '/') + 1);
            // /0 is used to denote the default route.  Since we're not doing routing,
            // we will use it for error checking
            if ($cidr < 1 || $cidr > 32) {
                throw new Zend_Validate_Exception('Invalid CIDR specified');
            }
            $this->_netmask = -1 << (32 - (int)$cidr);
 
        } else {
            $this->_subnet    = ip2long($subnet);
            $this->_netmask = ip2long($netmask);
        }
 
    }
 
    public function isValid ($value)
    {
        $this->_setValue($value);
        $host = ip2long($value);
 
        $check1 = $host & $this->_netmask;
        $check2 = $this->_subnet & $this->_netmask;
 
        if ($check1 == $check2) {
            return true;
        }
        $this->_error(self::NOT_ON_SUBNET);
        return false;
    }
}

 

Here is the Unit Test case

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
assertTrue($sn->isValid('192.168.0.2'));
    }
 
    public function testValidSubnetWithCIDR()
    {
        $sn = new Esc_Validate_Subnet('192.168.0.1/24');
        $this->assertTrue($sn->isValid('192.168.0.2'));
    }
 
    public function testInvalidSubnetAndNetmask()
    {
        $sn = new Esc_Validate_Subnet('192.168.1.1', '255.255.255.0');
        $this->assertFalse($sn->isValid('192.168.0.2'));
    }
 
    public function testInvalidSubnetWithCIDR()
    {
        $sn = new Esc_Validate_Subnet('192.168.1.1/24');
        $this->assertFalse($sn->isValid('192.168.0.2'));
    }
 
    /**
     * @expectedException Zend_Validate_Exception
     */
 
    public function testMalformedCIDRBadSeperator()
    {
        $sn = new Esc_Validate_Subnet('192.168.0.1_24');
        $this->assertTrue($sn->isValid('192.168.0.2'));
    }
 
    /**
     * @expectedException Zend_Validate_Exception
     */
 
    public function testMalformedCIDRNumericalCIDR()
    {
        $sn = new Esc_Validate_Subnet('192.168.0.1/abc');
        $this->assertTrue($sn->isValid('192.168.0.2'));
    }
 
    /**
     * @expectedException Zend_Validate_Exception
     */
 
    public function testMalformedCIDRLowCIDR()
    {
        $sn = new Esc_Validate_Subnet('192.168.0.1/-1');
        $this->assertTrue($sn->isValid('192.168.0.2'));
    }
    /**
     * @expectedException Zend_Validate_Exception
     */
 
    public function testMalformedCIDRHighCIDR()
    {
        $sn = new Esc_Validate_Subnet('192.168.0.1/33');
        $this->assertTrue($sn->isValid('192.168.0.2'));
    }
 
}

 

Feel free to peruse and see if I have made any mistakes.  I wrote it pretty quickly and so there is, in all likelihood, something that could be tweaked.

1 comments
Mark Droog
Mark Droog

Thank you very much for this code, I was almost scared that I was the only person who validates network/netmask addresses on being valid in PHP.

 

You saved me quite some work, THNX!!

Post Navigation

Web Analytics