OpenBSD-4.6/usr.sbin/smtpd/ruleset.c

Compare this file to the similar file:
Show the results in this format:

/*	$OpenBSD: ruleset.c,v 1.1 2009/06/01 22:51:47 gilles Exp $ */

/*
 * Copyright (c) 2009 Gilles Chehade <gilles@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/param.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <db.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "smtpd.h"

struct rule    *ruleset_match(struct smtpd *, struct path *, struct sockaddr_storage *);
int		ruleset_check_source(struct map *, struct sockaddr_storage *);
int		ruleset_match_mask(struct sockaddr_storage *, struct netaddr *);


struct rule *
ruleset_match(struct smtpd *env, struct path *path, struct sockaddr_storage *ss)
{
	struct rule *r;
	struct cond *cond;
	struct map *map;
	struct mapel *me;

	TAILQ_FOREACH(r, env->sc_rules, r_entry) {

		if (ss != NULL &&
		    (!(path->flags & F_PATH_AUTHENTICATED) &&
			! ruleset_check_source(r->r_sources, ss)))
			continue;

		TAILQ_FOREACH(cond, &r->r_conditions, c_entry) {
			if (cond->c_type == C_ALL)
				return r;

			if (cond->c_type == C_DOM) {
				cond->c_match = map_find(env, cond->c_map);
				if (cond->c_match == NULL)
					fatal("failed to lookup map.");

				map = cond->c_match;
				TAILQ_FOREACH(me, &map->m_contents, me_entry) {
					log_debug("matching: %s to %s",
					    path->domain, me->me_key.med_string);
					if (hostname_match(path->domain, me->me_key.med_string)) {
						return r;
					}
				}
			}
		}
	}

	return NULL;
}

int
ruleset_check_source(struct map *map, struct sockaddr_storage *ss)
{
	struct mapel *me;

	if (ss == NULL) {
		/* This happens when caller is part of an internal
		 * lookup (ie: alias resolved to a remote address)
		 */
		return 1;
	}

	TAILQ_FOREACH(me, &map->m_contents, me_entry) {

		if (ss->ss_family != me->me_key.med_addr.ss.ss_family)
			continue;

		if (ss->ss_len != me->me_key.med_addr.ss.ss_len)
			continue;

		if (ruleset_match_mask(ss, &me->me_key.med_addr))
			return 1;
	}

	return 0;
}

int
ruleset_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
{
	if (ss->ss_family == AF_INET) {
		struct sockaddr_in *ssin = (struct sockaddr_in *)ss;
		struct sockaddr_in *ssinmask = (struct sockaddr_in *)&ssmask->ss;

		if ((ssin->sin_addr.s_addr & ssinmask->sin_addr.s_addr) ==
		    ssinmask->sin_addr.s_addr)
			return (1);
		return (0);
	}

	if (ss->ss_family == AF_INET6) {
		struct in6_addr	*in;
		struct in6_addr	*inmask;
		struct in6_addr	 mask;
		int		 i;

		bzero(&mask, sizeof(mask));
		for (i = 0; i < (128 - ssmask->bits) / 8; i++)
			mask.s6_addr[i] = 0xff;
		i = ssmask->bits % 8;
		if (i)
			mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;

		in = &((struct sockaddr_in6 *)ss)->sin6_addr;
		inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;

		for (i = 0; i < 16; i++) {
			if ((in->s6_addr[i] & mask.s6_addr[i]) !=
			    inmask->s6_addr[i])
				return (0);
		}
		return (1);
	}

	return (0);
}