/* $Id: milter-greylist.h,v 1.46.2.3 2006/11/07 05:12:12 manu Exp $ */

/*
 * Copyright (c) 2004 Emmanuel Dreyfus
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by Emmanuel Dreyfus
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,  
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _MILTER_GREYLIST_H_
#define _MILTER_GREYLIST_H_

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <libmilter/mfapi.h>
#include "config.h"
#include "dump.h"

#define NUMLEN 20
#define QSTRLEN 1024
#define REGEXLEN 1024
#define HDRLEN 1024
#define HEADERNAME "X-Greylist"


#if defined(HAVE_GETNAMEINFO)
#define IPADDRSTRLEN	NI_MAXHOST
#elif defined(INET6_ADDRSTRLEN)
#define IPADDRSTRLEN	INET6_ADDRSTRLEN
#else
#define IPADDRSTRLEN	IPADDRLEN
#endif

typedef union {
	struct in_addr in4;
#ifdef AF_INET6
	struct in6_addr in6;
#endif
} ipaddr;

typedef union {
	struct sockaddr sa;
	struct sockaddr_in sin;
#ifdef AF_INET6
	struct sockaddr_in6 sin6;
#endif
} sockaddr_t;

#define SA(sa)		((struct sockaddr *)(sa))
#define SA4(sa)		((struct sockaddr_in *)(sa))
#define SADDR4(sa)	(&SA4(sa)->sin_addr)
#ifdef AF_INET6
#define SA6(sa)		((struct sockaddr_in6 *)(sa))
#define SADDR6(sa)	(&SA6(sa)->sin6_addr)
#endif

/* Notes:
 * -For IPv6 not using s6_addr32 as Solaris 8 for some reason has it only 
 *  defined for its kernel... 
 * -Using also first two characters in "from" and "rcpt" to distribute 
 *  potentially lot of triplets coming from a single host (first two chars 
 *  only because "<>" is the "shortest" email address)
 */
#define F2B(s) (tolower((int)*(s)) | (tolower((int)*((s)+1)) << 8))
#define F2B_SPICE(from, rcpt) (conf.c_lazyaw ? 0 : (F2B(from) ^ F2B(rcpt)))

#define BUCKET_HASH_V4(v4a, v4m, from, rcpt, bucket_count) 	\
  ((ntohl((v4a)->s_addr & (v4m)->s_addr)			\
    ^ F2B_SPICE(from, rcpt))					\
   % bucket_count) 

#ifdef AF_INET6
#define IN6CAST32(_a) ((uint32_t *)(&(_a)->s6_addr))

#define BUCKET_HASH_V6(v6a, v6m, from, rcpt, bucket_count)	\
  ((ntohl(IN6CAST32(v6a)[0] & IN6CAST32(v6m)[0]) ^		\
    ntohl(IN6CAST32(v6a)[1] & IN6CAST32(v6m)[1]) ^		\
    ntohl(IN6CAST32(v6a)[2] & IN6CAST32(v6m)[2]) ^		\
    ntohl(IN6CAST32(v6a)[3] & IN6CAST32(v6m)[3])		\
    ^ F2B_SPICE(from, rcpt))					\
   % bucket_count)

#define BUCKET_HASH(sa, from, rcpt, bucket_count)		\
  (sa->sa_family == AF_INET ?					\
   BUCKET_HASH_V4(SADDR4(sa), 					\
		  &conf.c_match_mask,				\
		  from, rcpt, bucket_count)			\
   : sa->sa_family == AF_INET6 ? 				\
   BUCKET_HASH_V6(SADDR6(sa),					\
		  &conf.c_match_mask6, 				\
		  from, rcpt, bucket_count)			\
   : 0)

#else /* AF_INET6 */

#define BUCKET_HASH(sa, from, rcpt, bucket_count) 		\
  (sa->sa_family == AF_INET ?					\
   BUCKET_HASH_V4(SADDR4(sa), 					\
		  &conf.c_match_mask,				\
		  from, rcpt, bucket_count)			\
   : 0)

#endif

struct mlfi_priv {
	sockaddr_t priv_addr;
	socklen_t priv_addrlen;
	char priv_hostname[ADDRLEN + 1];
	char priv_helo[ADDRLEN + 1];
	char priv_from[ADDRLEN + 1];
	char priv_rcpt[ADDRLEN + 1];
	time_t priv_elapsed;
	int priv_whitelist;
	char *priv_queueid;
	int priv_delayed_reject;
	time_t priv_remaining;
	int priv_acl_line;
	time_t priv_delay;
	time_t priv_autowhite;
	char *priv_code;
	char *priv_ecode;
	char *priv_msg;
};

sfsistat mlfi_connect(SMFICTX *, char *, _SOCK_ADDR *);
sfsistat mlfi_helo(SMFICTX *, char *);
sfsistat mlfi_envfrom(SMFICTX *, char **);
sfsistat mlfi_envrcpt(SMFICTX *, char **);
sfsistat mlfi_eom(SMFICTX *);
sfsistat mlfi_close(SMFICTX *);
void usage(char *);
int humanized_atoi(char *);
struct in_addr *prefix2mask4(int, struct in_addr *);
#ifdef AF_INET6
struct in6_addr *prefix2mask6(int, struct in6_addr *);
#endif
void cleanup_sock(char *);
void unmappedaddr(struct sockaddr *, socklen_t *);
void final_dump(void);
int main(int, char **);
void mg_log(int, char *, ...);
char *strncpy_rmsp(char *, char *, size_t);

#ifdef HAVE_STRLCAT
/* #include <string.h> */
#define mystrlcat strlcat
#else
size_t mystrlcat(char *, const char *src, size_t size);
#endif

#if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT))
#define MLFI_HELO mlfi_helo
#else
#define MLFI_HELO NULL
#endif


/*
 * Locking management
 */
#define WRLOCK(lock) {							  \
	int err;							  \
									  \
	if ((err = pthread_rwlock_wrlock(&(lock))) != 0) {		  \
		syslog(LOG_ERR, "%s:%d pthread_rwlock_wrlock failed: %s", \
		    __FILE__, __LINE__, strerror(err));			  \
		exit(EX_SOFTWARE);					  \
	}								  \
}

#define RDLOCK(lock) {							  \
	int err;							  \
									  \
	if ((err = pthread_rwlock_rdlock(&(lock))) != 0) {		  \
		syslog(LOG_ERR, "%s:%d pthread_rwlock_rdlock failed: %s", \
		    __FILE__, __LINE__, strerror(err));			  \
		exit(EX_SOFTWARE);					  \
	}								  \
}

/*
 * There is a bug in GNU pth-2.0.0 that will cause a spurious EPERM
 * error when a thread releases a read lock that has been shared by
 * two threads and already released by the other one. As a workaround
 * for that problem, we just avoid quitting on this error.
 */
#ifndef HAVE_BROKEN_RWLOCK
#define UNLOCK(lock) {							  \
	int err;							  \
									  \
	if ((err = pthread_rwlock_unlock(&(lock))) != 0) {		  \
		syslog(LOG_ERR, "%s:%d pthread_rwlock_unlock failed: %s", \
		    __FILE__, __LINE__, strerror(err));			  \
		exit(EX_SOFTWARE);					  \
	}								  \
}
#else
#define UNLOCK(lock) {							  \
	int err;							  \
									  \
	if ((err = pthread_rwlock_unlock(&(lock))) != 0) {		  \
		syslog(LOG_DEBUG, "%s:%d pthread_rwlock_unlock failed: "  \
		    "%s (ignored)", __FILE__, __LINE__, strerror(err));	  \
	}								  \
}
#endif

#ifdef HAVE_MISSING_TIMERADD
#define	timeradd(tvp, uvp, vvp)						\
	do {								\
		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
		if ((vvp)->tv_usec >= 1000000) {			\
			(vvp)->tv_sec++;				\
			(vvp)->tv_usec -= 1000000;			\
		}							\
	} while (/* CONSTCOND */ 0)
#define	timersub(tvp, uvp, vvp)						\
	do {								\
		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
		if ((vvp)->tv_usec < 0) {				\
			(vvp)->tv_sec--;				\
			(vvp)->tv_usec += 1000000;			\
		}							\
	} while (/* CONSTCOND */ 0)
#endif

#define ADD_REASON(whystr, reason)					\
	{								\
		if (whystr[0] != '\0')					\
			mystrlcat(whystr, ", ", sizeof(whystr));	\
		mystrlcat(whystr, reason, sizeof(whystr));		\
	}

#endif /* _MILTER_GREYLIST_H_ */



syntax highlighted by Code2HTML, v. 0.9.1