view src/context.h @ 264:56f55547b120

fix unauthenticated rate limit bug for empty mail from; move unauthenticated rate limit checks after spam filtering
author Carl Byington <carl@five-ten-sg.com>
date Sat, 21 Jul 2012 12:42:26 -0700
parents be939802c64e
children f941563c2a95
line wrap: on
line source

/*

Copyright (c) 2007 Carl Byington - 510 Software Group, released under
the GPL version 3 or any later version at your choice available at
http://www.gnu.org/licenses/gpl-3.0.txt

*/

#ifndef context_include
#define context_include

enum status {oksofar,       // not rejected yet
             white,         // whitelisted
             black,         // blacklisted
             reject};       // rejected by a dns list

class DNSBL;
class DNSWL;
class CONTEXT;
class VERIFY;
class SMTP;
class WHITELISTER;
class DELAYWHITE;
class recorder;

typedef map<const char *, const char *, ltstr>  string_map;
typedef set<int>                                int_set;
typedef set<int32_t>                            int32_t_set;
typedef list<SMTP *>                            smtp_list;
typedef DNSBL *                                 DNSBLP;
typedef DNSWL *                                 DNSWLP;
typedef VERIFY *                                VERIFYP;
typedef WHITELISTER *                           WHITELISTERP;
typedef DELAYWHITE *                            DELAYWHITEP;
typedef list<DNSBLP>                            dnsblp_list;
typedef map<const char *, DNSBLP, ltstr>        dnsblp_map;
typedef list<DNSWLP>                            dnswlp_list;
typedef map<const char *, DNSWLP, ltstr>        dnswlp_map;
typedef CONTEXT *                               CONTEXTP;
typedef list<CONTEXTP>                          context_list;
typedef map<const char *, CONTEXTP, ltstr>      context_map;
typedef map<const char *, int32_t, ltstr>       ns_mapper;  // name to ipv4 address
typedef map<const char *, int, ltstr>           rcpt_rates;
typedef map<const char *, time_t,  ltstr>       autowhite_sent;
typedef map<const char *, VERIFYP, ltstr>       verify_map;
typedef map<const char *, WHITELISTERP, ltstr>  whitelister_map;
typedef list<DELAYWHITEP>                       delay_whitelist;

class SMTP {
    static const int maxlen = 1000;
    int     fd;
    bool    error;
    time_t  stamp;
    char    efrom[maxlen];  // last envelope from sent on this socket
    int     pending;        // unread bytes in buffer, not including the null terminator
    char    buffer[maxlen];
public:
    SMTP(int f)             {fd = f; error = false; now(); efrom[0] = '\0'; init();};
    ~SMTP()                 {if (!error) quit(); closefd();};
    void    init()          {pending = 0; buffer[0] = '\0';};
    void    append(const char *c)   {strncat(buffer, c, max(0, maxlen-1-(int)strlen(c)));};
    bool    err()           {return error;};
    void    now()           {stamp = time(NULL);};
    time_t  get_stamp()     {return stamp;};
    int     get_fd()        {return fd;};
    int     writer();
    int     reader();
    int     read_line();
    int     read_response();
    void    flush_line(int r);
    int     cmd(const char *c);
    int     helo();
    int     rset();
    int     from(const char *f);
    int     rcpt(const char *t);
    int     quit();
    void    closefd();
#ifdef VERIFY_DEBUG
    static void log(const char *m, int v);
    static void log(const char *m, const char *v);
#endif
};

class VERIFY {
    const char      *host;      // host to be used to verify recipient addresses
    time_t          last_err;   // time of last socket error
    pthread_mutex_t mutex;      // protect the lists of sockets and timestamps
    smtp_list       connections;// open sockets, ready to be used
public:
    VERIFY(const char *h);
    void    closer();           // if the oldest socket is ancient, close it
    SMTP    *get_connection();
    void    put_connection(SMTP *conn);
    bool    ok(const char *from, const char *to);
};

class WHITELISTER {
    const char      *fn;        // file to use
    int             days;       // how long do we keep entries
    pthread_mutex_t mutex;      // protect the flag and map
    time_t          loaded;     // when we loaded this file
    bool            need;       // force writing on new entries
    autowhite_sent  rcpts;      // recipient map to remember when we sent them mail
public:
    WHITELISTER(const char *f, int d);
    void    merge();
    void    writer();           // dump any changes back to the file
    void    sent(const char *to);
    bool    is_white(const char *from); // should we white list this sender (did we send them anything recently)
    int     get_days()      {return days;};
    void    set_days(int d) {days = d;};
};

class DELAYWHITE {
    const char     *loto;
    WHITELISTERP    w;
    CONTEXTP        con;
public:
    DELAYWHITE(const char *loto_, WHITELISTERP w_, CONTEXTP con_);
    const char     *get_loto() {return loto;};
    WHITELISTERP    get_w()    {return w;};
    CONTEXTP        get_con()  {return con;};
};

struct DNSBL {
    const char    *name;    // nickname for this dns based list
    const char    *suffix;  // blacklist suffix like blackholes.five-ten-sg.com
    const char    *message; // error message with one or two %s operators for the ip address replacement
    DNSBL(const char *n, const char *s, const char *m);
    bool operator==(const DNSBL &rhs);
};

struct DNSWL {
    const char    *name;    // nickname for this dns based list
    const char    *suffix;  // whitelist suffix like list.dnswl.org
    int            level;   // matches 127.0.x.y where y >= level
    DNSWL(const char *n, const char *s, const int l);
    bool operator==(const DNSWL &rhs);
};

class CONTEXT {
    CONTEXTP        parent;
    const char *    name;
    context_map     children;           // map child context names to their contexts
    string_set      env_to;             // this context applies to these envelope recipients
    const char *    verify_host;        // use this smtp host to verify email addresses
    VERIFYP         verifier;           // pointer to the verifier structure
    const char *    generic_regx;       // pointer to generic regular expression
    const char *    generic_message;    // pointer to generic message with one %s
    regex_t         generic_pattern;    // compiled regex pattern
    const char *    white_regx;         // pointer to whitelist regular expression
    regex_t         white_pattern;      // compiled regex pattern
    const char *    autowhite_file;     // file to use for automatic whitelisting
    WHITELISTERP    whitelister;        // pointer to the auto whitelister structure
    string_map      env_from;           // map senders to white/black/unknown
    context_map     env_from_context;   // map senders to a child context
    const char *    env_from_default;   // default value for senders that are not found in the map white/black/unknown/inherit
    bool            content_filtering;  //
    const char *    content_suffix;     // for url body filtering based on ip addresses of hostnames in the body
    const char *    content_message;    // ""
    const char *    uribl_suffix;       // for uribl body filtering based on hostnames in the body
    const char *    uribl_message;      // ""
    string_set      content_host_ignore;// hosts to ignore for content sbl checking
    string_set      content_tlds;       //
    string_set      content_cctlds;     //
    string_set      html_tags;          // set of valid html tags
    int             host_limit;         // limit on host names
    const char *    host_limit_message; // error message for excessive host names
    bool            host_random;        // pick a random selection of host names rather than error for excessive hosts
    int             tag_limit;          // limit on bad html tags
    const char *    tag_limit_message;  // error message for excessive bad html tags
    int             spamassassin_limit; // max score from spamassassin
    bool            require_match;      // require matching context filtering context
    bool            dcc_greylist;       // should we do dcc greylisting?
    int             dcc_bulk_threshold; // off = 0, many = 1000
    dnsblp_map      dnsbl_names;        // name to dnsbl mapping for lists that are available in this context and children
    dnsblp_list     dnsbl_list;         // list of dnsbls to be used in this context
    bool            dnsbl_list_parsed;  // true iff we have actually parsed a dnsbl_list
    dnswlp_map      dnswl_names;        // name to dnswl mapping for lists that are available in this context and children
    dnswlp_list     dnswl_list;         // list of dnswls to be used in this context
    bool            dnswl_list_parsed;  // true iff we have actually parsed a dnswl_list
    int             default_rcpt_rate;  // if not specified per user
    int             rcpt_daily_multiple;// daily multiplier applied to hourly rate
    rcpt_rates      rcpt_per_hour;      // per user limits on number of recipients per hour


public:
    CONTEXT(CONTEXTP parent_, const char *name_);
    ~CONTEXT();
    CONTEXTP    get_parent()                                {return parent;};
    bool        is_parent(CONTEXTP p);      // is p a parent of this?
    const char* get_full_name(char *buffer, int size);
    void        add_context(CONTEXTP child)                 {children[child->name] = child;};
    bool        allow_env_to(const char *to)                {return (parent) ? parent->cover_env_to(to) : true;};
    bool        cover_env_to(const char *to);

    void        set_verifier(VERIFYP v)                     {verifier    = v;};
    void        set_verify(const char *host)                {verify_host = host;};
    const char* get_verify()                                {return verify_host;};
    VERIFYP     find_verify(const char *to);


    void        set_whitelister(WHITELISTERP v)             {whitelister    = v;};
    void        set_autowhite(const char *fn)               {autowhite_file = fn;};
    const char* get_autowhite()                             {return autowhite_file;};
    WHITELISTERP find_autowhite(const char *from, const char *to);

    void        set_default_rate(int limit)                 {default_rcpt_rate   = limit;};
    void        set_daily_multiple(int multiple)            {rcpt_daily_multiple = multiple;};
    void        add_rate(const char *user, int limit)       {rcpt_per_hour[user] = limit;};
    int         find_rate(const char *user);
    bool        is_unauthenticated_limited(const char *user);
    int         get_daily_multiple()                        {return rcpt_daily_multiple;};

    void        add_to(const char *to)                            {env_to.insert(to);};
    void        add_from(const char *from, const char *status)    {env_from[from] = status;};
    void        add_from_context(const char *from, CONTEXTP con)  {env_from_context[from] = con;};
    void        set_from_default(const char *status)              {env_from_default = status;};
    const char* find_from(const char *from, bool update_white = false, const char *queueid = NULL);
    CONTEXTP    find_context(const char *from);
    CONTEXTP    find_from_context_name(const char *name);

    void        set_content_filtering(bool filter)          {content_filtering = filter;      };
    void        set_content_suffix(const char *suffix)      {content_suffix    = suffix;      };
    void        set_content_message(const char *message)    {content_message   = message;     };
    void        set_uribl_suffix(const char *suffix)        {uribl_suffix      = suffix;      };
    void        set_uribl_message(const char *message)      {uribl_message     = message;     };
    void        add_ignore(const char *host)                {content_host_ignore.insert(host);};
    void        add_tld(const char *tld)                    {content_tlds.insert(tld);        };
    void        add_cctld(const char *cctld)                {content_cctlds.insert(cctld);    };

    void        set_host_limit(int limit)                   {host_limit         = limit;  };
    void        set_host_message(const char *message)       {host_limit_message = message;};
    void        set_host_random(bool random)                {host_random        = random; };
    void        set_spamassassin_limit(int limit)           {spamassassin_limit = limit;  };
    void        set_tag_limit(int limit)                    {tag_limit          = limit;  };
    void        set_tag_message(const char *message)        {tag_limit_message  = message;};
    void        add_tag(const char *tag)                    {html_tags.insert(tag);       };

    void        add_dnsbl(const char *name, DNSBLP dns)     {dnsbl_names[name] = dns;  };
    void        add_dnsbl(DNSBLP dns)                       {dnsbl_list.push_back(dns);};
    DNSBLP      find_dnsbl(const char *name);
    void        set_dnsbll_parsed()                         {dnsbl_list_parsed = true;};

    void        add_dnswl(const char *name, DNSWLP dns)     {dnswl_names[name] = dns;  };
    void        add_dnswl(DNSWLP dns)                       {dnswl_list.push_back(dns);};
    DNSWLP      find_dnswl(const char *name);
    void        set_dnswll_parsed()                         {dnswl_list_parsed = true;};

    bool        set_white(const char *regx);
    bool        white_match(const char *from);

    bool        set_generic(const char *regx, const char *msg);
    const char* generic_match(const char *client);

    void        set_require(bool r)                         {require_match      = r; };
    void        set_grey(bool g)                            {dcc_greylist       = g; };
    void        set_bulk(int b)                             {dcc_bulk_threshold = b; };

    bool            get_content_filtering()                 {return content_filtering; };
    bool            get_require()                           {return content_filtering && require_match;     };
    bool            get_grey()                              {return content_filtering && dcc_greylist;      };
    int             get_bulk()                              {return (content_filtering) ? dcc_bulk_threshold : 0;};
    int             get_host_limit()                        {return (content_filtering) ? host_limit         : 0;};
    bool            get_host_random()                       {return (content_filtering) ? host_random        : 0;};
    int             get_spamassassin_limit()                {return (content_filtering) ? spamassassin_limit : 0;};
    const char*     get_content_suffix();
    const char*     get_content_message();
    const char*     get_uribl_suffix();
    const char*     get_uribl_message();
    string_set&     get_content_host_ignore();
    string_set&     get_content_tlds();
    string_set&     get_content_cctlds();
    string_set&     get_html_tags();
    dnsblp_list&    get_dnsbl_list();
    dnswlp_list&    get_dnswl_list();

    bool        acceptable_content(recorder &memory, int score, int bulk, string& msg);
    bool        ignore_host(const char *host);

    void        dump(bool isdefault, bool &spamass, int level = 0);
};


struct CONFIG {
    // the only mutable stuff once it has been loaded from the config file
    int         reference_count;    // protected by the global config_mutex
    // all the rest is constant after loading from the config file
    int             generation;
    time_t          load_time;
    string_set      config_files;
    context_list    contexts;       // owns all the contexts, not just top level contexts
    context_map     env_to;         // map recipient to a filtering context
    CONTEXTP        default_context;// for env_to values that don't have their own specific filtering context
    // the default context is also used for some of the content filtering values

    CONFIG();
    ~CONFIG();
    void        add_context(CONTEXTP con);
    void        add_to(const char *to, CONTEXTP con);
    CONTEXTP    find_context(const char *to);
    void        dump();
};


extern const char *token_autowhite;
extern const char *token_black;
extern const char *token_cctld;
extern const char *token_content;
extern const char *token_context;
extern const char *token_dccbulk;
extern const char *token_dccfrom;
extern const char *token_dccgrey;
extern const char *token_dccto;
extern const char *token_default;
extern const char *token_dnsbl;
extern const char *token_dnsbll;
extern const char *token_dnswl;
extern const char *token_dnswll;
extern const char *token_envfrom;
extern const char *token_envto;
extern const char *token_filter;
extern const char *token_generic;
extern const char *token_host_limit;
extern const char *token_html_limit;
extern const char *token_html_tags;
extern const char *token_ignore;
extern const char *token_include;
extern const char *token_inherit;
extern const char *token_lbrace;
extern const char *token_mailhost;
extern const char *token_many;
extern const char *token_no;
extern const char *token_off;
extern const char *token_ok;
extern const char *token_ok2;
extern const char *token_on;
extern const char *token_rate;
extern const char *token_rbrace;
extern const char *token_require;
extern const char *token_semi;
extern const char *token_soft;
extern const char *token_spamassassin;
extern const char *token_substitute;
extern const char *token_tld;
extern const char *token_unknown;
extern const char *token_uribl;
extern const char *token_verify;
extern const char *token_white;
extern const char *token_white_regex;
extern const char *token_yes;

extern pthread_mutex_t verifier_mutex;     // protect the verifier map
extern pthread_mutex_t whitelister_mutex;  // protect the

void discard(string_set &s);
const char* register_string(string_set &s, const char *name);
const char* register_string(const char *name);
void  clear_strings();
bool  load_conf(CONFIG &dc, const char *fn);
void* verify_closer(void *arg);
void* whitelister_writer(void *arg);
void  token_init();

#endif