NetBSD/gnu/dist/postfix/util/match_ops.c

230 lines
5.4 KiB
C

/*++
/* NAME
/* match_ops 3
/* SUMMARY
/* simple string or host pattern matching
/* SYNOPSIS
/* #include <match_ops.h>
/*
/* int match_string(string, pattern)
/* const char *string;
/* const char *pattern;
/*
/* int match_hostname(name, pattern)
/* const char *name;
/* const char *pattern;
/*
/* int match_hostaddr(addr, pattern)
/* const char *addr;
/* const char *pattern;
/* DESCRIPTION
/* This module implements simple string and host name or address
/* matching. The matching process is case insensitive. If a pattern
/* has the form type:name, table lookup is used instead of string
/* or address comparison.
/*
/* match_string() matches the string against the pattern, requiring
/* an exact (case-insensitive) match.
/*
/* match_hostname() matches the host name when the hostname matches
/* the pattern exactly, or when the pattern matches a parent domain
/* of the named host.
/*
/* match_hostaddr() matches a host address when the pattern is
/* identical to the host address, or when the pattern is a net/mask
/* that contains the address. The mask specifies the number of
/* bits in the network part of the pattern.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffff
#endif
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <split_at.h>
#include <dict.h>
#include <match_ops.h>
#include <stringops.h>
/* match_string - match a string literal */
int match_string(const char *string, const char *pattern)
{
const char *myname = "match_string";
int match;
char *key;
if (msg_verbose)
msg_info("%s: %s ~? %s", myname, string, pattern);
/*
* Try dictionary lookup: exact match.
*/
if (strchr(pattern, ':') != 0) {
key = lowercase(mystrdup(string));
match = (dict_lookup(pattern, key) != 0);
myfree(key);
if (match != 0)
return (1);
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", pattern);
return (0);
}
/*
* Try an exact string match.
*/
if (strcasecmp(string, pattern) == 0) {
return (1);
}
/*
* No match found.
*/
return (0);
}
/* match_hostname - match a host by name */
int match_hostname(const char *name, const char *pattern)
{
const char *myname = "match_hostname";
const char *pd;
const char *entry;
char *next;
char *temp;
int match;
if (msg_verbose)
msg_info("%s: %s ~? %s", myname, name, pattern);
/*
* Try dictionary lookup: exact match and parent domains.
*/
if (strchr(pattern, ':') != 0) {
temp = lowercase(mystrdup(name));
match = 0;
for (entry = temp; /* void */ ; entry = next + 1) {
if ((match = (dict_lookup(pattern, entry) != 0)) != 0)
break;
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", pattern);
if ((next = strchr(entry, '.')) == 0)
break;
}
myfree(temp);
return (match);
}
/*
* Try an exact match with the host name.
*/
if (strcasecmp(name, pattern) == 0) {
return (1);
}
/*
* See if the pattern is a parent domain of the hostname.
*/
else {
pd = name + strlen(name) - strlen(pattern);
if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0)
return (1);
}
return (0);
}
/* match_parse_mask - parse net/mask pattern */
static int match_parse_mask(const char *pattern, unsigned long *net_bits,
int *mask_shift)
{
char *saved_pattern;
char *mask;
#define BITS_PER_ADDR 32
saved_pattern = mystrdup(pattern);
if ((mask = split_at(saved_pattern, '/')) != 0) {
if ((*mask_shift = atoi(mask)) <= 0 || *mask_shift > BITS_PER_ADDR
|| (*net_bits = inet_addr(saved_pattern)) == INADDR_NONE) {
msg_fatal("bad net/mask pattern: %s", pattern);
}
}
myfree(saved_pattern);
return (mask != 0);
}
/* match_hostaddr - match host by address */
int match_hostaddr(const char *addr, const char *pattern)
{
const char *myname = "match_hostaddr";
int mask_shift;
unsigned long mask_bits;
unsigned long net_bits;
unsigned long addr_bits;
if (msg_verbose)
msg_info("%s: %s ~? %s", myname, addr, pattern);
if (addr[strspn(addr, "01234567890./:")] != 0)
return (0);
/*
* Try dictionary lookup. This can be case insensitive. XXX Probably
* should also try again after stripping least significant octets.
*/
if (strchr(pattern, ':') != 0) {
if (dict_lookup(pattern, addr) != 0)
return (1);
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", pattern);
return (0);
}
/*
* Try an exact match with the host address.
*/
if (strcasecmp(addr, pattern) == 0) {
return (1);
}
/*
* In a net/mask pattern, the mask is specified as the number of bits of
* the network part.
*/
if (match_parse_mask(pattern, &net_bits, &mask_shift)) {
addr_bits = inet_addr(addr);
if (addr_bits == INADDR_NONE)
msg_fatal("%s: bad address argument: %s", myname, addr);
mask_bits = htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift));
return ((addr_bits & mask_bits) == (net_bits & mask_bits));
}
return (0);
}