/* $Id: ncat_hostmatch.c 9793 2008-08-28 04:52:43Z kris $ */

#include "nsock.h"
#include "ncat.h"
#include "sys_wrap.h"

#include <stdio.h>
#include <fcntl.h>

/* 
 * See if the IP address mask matches the remote address.
 *
 * Acceptable address formats: 
 *   ip.ip.ip.ip
 *   ip.ip.ip.ip/cidr mask
 *   ip.ip.ip.ip:nm.nm.nm.nm
 *   ip.ip.ip.*
 *   ip.ip.*.*
 *   ip.*.*.*
 *
 * Return 0 - No address match or a NULL IP address was supplied.
 * Return 1 - The address matched.
 */
int ncat_hostmatch(char *addr, char *remoteaddr)
{
    int found = 1,  not_found = 0,  i,  a[4], m[4], r[4],   mask;

    /* No address supplied, therefore it doesn't match. */
    if (addr == NULL)
        return 0;

    /* Negate(!) match */
    if (*addr == '!') {
        /* Switch matching cases around. */
        found = 0;
        not_found = 1;
	
        /* Skip negation(!) */
        addr++;
    }

    /* CIDR delimited netmask, of the form: 'ip.ip.ip.ip/nn' */
    if (Sscanf(addr, "%d.%d.%d.%d/%d", a, a + 1, a + 2, a + 3, &mask) == 5) {
        zmem(m, sizeof(m));

        /* validate mask bounds */
        if (mask < 0)
            mask = 0;
	    else if (mask > 32)
            mask = 32;

        /* setup netmask */
        for (i = 0; mask > 8; i++, mask -= 8)
            m[i] = 255;

        switch (mask) {
            case 8:
                m[i] += 1;
            case 7:
                m[i] += 2;
            case 6:
                m[i] += 4;
            case 5:
                m[i] += 8;
            case 4:
                m[i] += 16;
            case 3:
                m[i] += 32;
            case 2:
                m[i] += 64;
            case 1:
                m[i] += 128;
                break;
            default:
                bye("Invalid mask %d", mask);
                break;
        }

        /* Decimalise remote address */
        Sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3);

        /* Got a match? */
        for (i = 0; i < 4; i++)
            if ((a[i] & m[i]) != (r[i] & m[i]))
                return not_found;
	
        return found;
    }else if (Sscanf(addr, "%d.%d.%d.%d:%d.%d.%d.%d", a, a + 1, a + 2, a + 3, m,
	                    m + 1, m + 2, m + 3) == 8) {
        
        /* Colon delimited IP Address mask, of the form: 'ip.ip.ip.ip:nm.nm.nm.nm' */
        Sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3);

        /* Got a match? */
        for (i = 0; i < 4; i++)
            if ((a[i] & m[i]) != (r[i] & m[i]))
                return not_found;
        
        return found;
    }else if (Sscanf(addr, "%d.%d.%d.%d", a, a + 1, a + 2, a + 3) == 4) {
        
        /* Plain old IP address, no subnet mask */
        Sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3);

        /* Got a match? */
        for (i = 0; i < 4; i++)
            if (a[i] != r[i])
                return not_found;

        return found;
    }else if (Sscanf(addr, "%d.%d.%d.*", a, a + 1, a + 2) == 3) {
    
        /* Wildcarded IP address, of the form 'ip.ip.ip.*' */
        Sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3);

        /* Got a match? */
        for (i = 0; i < 3; i++)
            if (a[i] != r[i])
                return not_found;
	
        return found;
    }else if (Sscanf(addr, "%d.%d.*.*", a, a + 1) == 2) {
    
        /* Wildcarded IP address, of the form: 'ip.ip.*.*' */
        Sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3);

        /* Got a match? */
        for (i = 0; i < 2; i++)
            if (a[i] != r[i])
                return not_found;
	
        return found;
    }else if (Sscanf(addr, "%d.*.*.*", a) == 1) {
    
        /* Wildcarded IP address, of the form: 'ip.*.*.*' */
        Sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3);

        /* Got a match? */
        for (i = 0; i < 1; i++)
            if (a[i] != r[i])
                return not_found;
	
        return found;
    }
    
    return not_found;
}
