/*
 *
 *
 *                           Policy Daemon
 *
 *  policy daemon is used in conjuction with postfix to combat spam.
 *
 *  Copyright (C) 2007 Nigel Kukard <nkukard@lbsd.net>
 *  Copyright (C) 2004 Cami Sardinha (cami@mweb.co.za)
 *
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the  GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the License, or (at your
 *  option) any later version.
 *
 *  This program  is  distributed  in the hope that  it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 *
 */

/* INCLUDES */
#include <ctype.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <syslog.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdio.h>
#include <mysql.h>
#include <setjmp.h>

/* SIGPIPE quirks */
#ifndef MSG_NOSIGNAL
  /* Operating systems which have SO_NOSIGPIPE but not MSG_NOSIGNAL */
  #if defined (__FreeBSD__) || defined (__OpenBSD__) || defined(__APPLE__)
    #define MSG_NOSIGNAL SO_NOSIGPIPE
  /* Some versions of NetBSD dont have SO_NOSIGPIPE, check if we can use it or define as 0 */
  #elif defined(__NetBSD__)
    #ifdef SO_NOSIGPIPE
      #define MSG_NOSIGNAL SO_NOSIGPIPE
    #else
      #define MSG_NOSIGNAL 0
    #endif
  #else
    #error Your OS doesnt support MSG_NOSIGNAL or SO_NOSIGPIPE, please report to policyd-devel@lists.sf.net
  #endif
#endif

/* CONFIGS */
#define PROJECT         "policyd"
#define VERSION         "v1.82"

/* Miscellaneous constants */
#define LISTENQ         1023    /* 2nd argument to listen() */
#define MAXLINE         1023    /* max text line length */
#define BUFFSIZE        8191    /* buffer size for reads and writes */
#define BUFSIZE         4095 
#ifndef MAXFDS
  #define MAXFDS        1023    /* max file descriptors   */
#endif

#define POSTFIX_X_HEADER        "action=prepend X-Greylist: Passed"
#define POSTFIX_GOOD            "action=dunno\n\n"
#define POSTFIX_GREYLIST        "action=defer_if_permit Policy Rejection-"
#define POSTFIX_BAD_SIZE        "action=reject Policy Rejection-"
#define POSTFIX_SPAMTRAP        "action=reject Policy Rejection-"
#define POSTFIX_BLACKLIST_PERM  "action=reject Policy Rejection-"
#define POSTFIX_BLACKLIST_TEMP  "action=defer_if_permit Policy Rejection-"
#define POSTFIX_MODULE_FAILURE  "action=defer_if_permit Policy Rejection- Invalid data\n\n"
#define POSTFIX_QUOTA_EXCEEDED_PERM  "action=reject Policy Rejection-"
#define POSTFIX_QUOTA_EXCEEDED_TEMP  "action=defer_if_permit Policy Rejection-"


/* MySQL VARIABLES */
char *MYSQLHOST;
char *MYSQLUSER;
char *MYSQLPASS;
char *MYSQLDBASE;
char *MYSQLOPT;
 int MYSQLPORT;

 
/* GLOBAL OPTARGS */
 char *configpath;
 int DEBUG;
 int DAEMON;
 int FAILSAFE;
 int TRIPLET_TIME;
 int TRIPLET_AUTH_TIMEOUT;
 int TRIPLET_UNAUTH_TIMEOUT;
 int OPTINOUT;
 int OPTINOUTALL;
 int TRAINING_MODE;
 int TRAINING_POLICY_TIMEOUT;
 int AUTO_WHITE_LISTING;
 int AUTO_WHITELIST_NUMBER;
 int AUTO_BLACKLIST_NUMBER;
 int AUTO_WHITELIST_EXPIRE;
 int AUTO_BLACKLIST_EXPIRE;
 int AUTO_WHITELIST_NETBLOCK;
 int SPAMTRAP_AUTO_EXPIRE;
 int WHITELISTING;
 int WHITELISTNULL;
 int WHITELISTSENDER;
 int WHITELISTDNSNAME;
 int BLACKLIST_TEMP_REJECT;
 int BLACKLISTING;
 int BLACKLIST_TIMEOUT;
 int BLACKLIST_NETBLOCK;
 int BLACKLIST_HELO;
 int BLACKLIST_HELO_AUTO_EXPIRE;
 int BLACKLISTSENDER;
 int BLACKLISTDNSNAME;
 int AUTO_BLACK_LISTING;
 int GREYLISTING;
 int SPAMTRAPPING;
 int HELO_CHECK;
 int HELO_MAX_COUNT;
 int HELO_BLACKLIST_AUTO_EXPIRE;
 int HELO_AUTO_EXPIRE;
 int GREYLIST_X_HEADER;
 unsigned int GREYLIST_HOSTADDR;
 int BINDPORT;
 int QUOTA_EXCEEDED_TEMP_REJECT;
 int SENDERTHROTTLE;
 int SENDER_THROTTLE_SASL;
 int SENDER_THROTTLE_HOST;
 int SENDERMSGLIMIT;
 int SENDERRCPTLIMIT;
 int SENDERTIMELIMIT;
 int SENDERQUOTALIMIT;
 int SENDERMSGSIZE;
 int SENDER_INACTIVE_EXPIRE;
 int SENDER_THROTTLE_AUTOBLACKLIST;
 int SENDER_THROTTLE_AUTOBLACKLIST_NUMBER;
 int SENDER_THROTTLE_AUTOBLACKLIST_EXPIRE;

 int RECIPIENTTHROTTLE;
 int RECIPIENTMSGLIMIT;
 int RECIPIENTTIMELIMIT;
 int RECIPIENT_INACTIVE_EXPIRE;
 int SYSLOG_FACILITY;
 int DATABASE_KEEPALIVE;
 int count;
 uid_t UID;
 gid_t GID;
 char *BINDHOST;
 char *CONN_ACL;
 char *CHROOT;
 char *PIDFILE;
 char *postfix_greylist;
 char *postfix_bad_size;
 char *postfix_spamtrap;
 char *postfix_blacklist;
 char *postfix_sender_quota_exceeded;
 char *postfix_recipient_quota_exceeded;

 
/* GLOBAL VARIABLES/ARRAYS */
 MYSQL * volatile mysql;
unsigned long int rcpt_count;           /* total mails processed */
unsigned long int mysql_failure_count;  /* total mysql queries   */
unsigned long int last_mail_time;       /* seconds since epoch   */
unsigned long int last_mysql_failure;   /* seconds since epoch   */
unsigned long int mysql_timeout;        /* mysql query timeout   */
       sigjmp_buf sjmp;

  int msock;                            /* master server socket  */
  int ssock;                            /* slave server socket   */
  int         mysql_array[MAXFDS][10];
  int      mysql_optarray[MAXFDS][1];
  
  char    mysqlchar_array[MAXFDS][20][64];
  char         host_array[MAXFDS][20][64];
  char       policy_array[MAXFDS][20][64];
  char      triplet_array[MAXFDS][20][64];

  char   mysqlquery_array[MAXFDS][512];
  char    xgreylist_array[MAXFDS][128];
  char      extract_array[MAXFDS][64];
  char   extract_ip_array[MAXFDS][64];
  char  extract_host_addr[MAXFDS][64];
  char        return_code[MAXFDS][64];
  char extract_array_conf[MAXFDS][64];
  
  unsigned int i[MAXFDS], instance_inc[MAXFDS], t;
  unsigned int tcount[MAXFDS], tquota[MAXFDS], trcpt[MAXFDS];
  char      tattrib_array[MAXFDS][1];
  int x[MAXFDS], y[MAXFDS];
  struct timeval timevalue;     /* gettimeofday() */
  unsigned int timenow;
  FILE *fd_config, *pidfile;
  int  action_array[MAXFDS];
  char confbuf[256];
  char buf[MAXFDS][MAXLINE];
  unsigned int buf_size[MAXFDS];
  unsigned int buf_counter[MAXFDS];


/*  PROTOTYPES */

  // sockets
  int w_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  int w_socket(int family, int type, int protocol);
  int w_accept(unsigned int fd, struct sockaddr *sa, socklen_t *salenptr);
   void buf_write(unsigned int fd, const char *ptr, size_t nbytes);
  int socktimeout(unsigned int fd, unsigned int sec);
  int daemonize(int nochdir, int noclose);
  int w_tcp_conn_acl (const char *host);
  int    cidr_ip_match (unsigned long ip, char *range);
  pid_t w_fork(void);
const char *w_inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
 ssize_t w_read(unsigned int fd, char *ptr, size_t max_size);
 ssize_t w_write(unsigned int fd, const void *vbuf);
 ssize_t f_write(unsigned int volatile fd, const void *vptr, size_t n);
 void w_close(unsigned int fd);
 void w_bind(unsigned int fd, const struct sockaddr *sa, socklen_t salen);
 void w_listen(unsigned int fd, unsigned int backlog);
 void sigalrm_handler (void);

  // functions
  void chk_pol(unsigned int fd);
  int bindsock(unsigned int port, unsigned int qlen);
  int greylist_check(unsigned int fd);
  int spamtrap_check(unsigned int fd);
  int throttle_check(unsigned int fd);
  int throttle_host(unsigned int fd);
  int throttle_from(unsigned int fd);
  int throttle_sasl(unsigned int fd);
  int throttle_rcpt(unsigned int fd);
  int helo_check(unsigned int fd);
  int module_info_check(unsigned int fd);
  int blacklist_helo_check(unsigned int fd);
  int gettime(void);
  int db_failure(unsigned int fd, char *module);
  int whitelist_check(unsigned int fd);
  int whitelist_sender_check(unsigned int fd);
  int whitelist_dnsname_check(unsigned int fd);
  int blacklist_sender_check(unsigned int fd);
  int blacklist_dnsname_check(unsigned int fd);
  void policy_reply(unsigned int fd, int code, int status);
  int blacklist_check(unsigned int fd);
  int extract_seconds(char *token);
  int parse_syslog_priority (char *str);
  int database_probe(unsigned int fd);
  int w_string_strip(void *str, char *token);
 void drop_privs(void);
 void read_conf(unsigned int prog);
 void logmessage(const char *fmt, ...);
 void usage(char *usag);
 void clear_var(unsigned int fd);
 void parse_buf(unsigned int fd, char *buf);
 void fold();
 void extract (unsigned int fd, char *token, unsigned int startlen);
 void extract_ip(unsigned int fd, char *token);
 void extract_ipfill(unsigned int fd, char *token);
 void extract_conf(unsigned int fd, char *token, unsigned int startlen);
 void syslog_token_set (char *token, int *value);
 char *strip_space (char *str);
  
 
  // mysql
 int db_doquery(unsigned int volatile fd);
 int db_optquery(unsigned int volatile fd);
 int db_charquery(unsigned int volatile fd);
 int db_printquery(unsigned int volatile fd);
 int db_deletequery(unsigned int volatile fd);
 int w_mysql_query(unsigned int volatile fd, const char *function);
MYSQL *db_connect(const char *dbname);



/* EOF */


syntax highlighted by Code2HTML, v. 0.9.1