/************************************************************ ** $Id: edit.c,v 0.8 2003/12/15 15:03:33 bcx Exp bcx $ ** ** Copyright Bryan Costales and Perforce Software, Inc. 2003 ** ** This code is "open source" as defined by version 1.9 of ** the Open Source Definition from: ** ** http://www.opensource.org/docs/definition.php. ** ** The is the slowedit interface for the milter. ** *************************************************************/ # define EXTERN extern # include "slow.h" extern PRIV *Priv; /* in test.c */ extern struct smfi_str Ctx; /* in test.c */ typedef struct { char *token; int mode; } EDITCOMMANDS; EDITCOMMANDS EditCommands[] = { # define EDITCOM_ADD 1 {"add", EDITCOM_ADD}, /* add an IP event */ # define EDITCOM_DELETE 2 {"delete", EDITCOM_DELETE}, /* delete an IP and all its events */ # define EDITCOM_LIST 3 {"list", EDITCOM_LIST}, /* list all or selected IP addresses */ # define EDITCOM_FIND 4 {"find", EDITCOM_FIND}, /* list all or selected IP addresses */ # define EDITCOM_ALIAS 5 {"alias", EDITCOM_ALIAS}, /* add an IP alias */ # define EDITCOM_WHITE 6 {"whitelist", EDITCOM_WHITE}, /* whitelist an IP address */ # define EDITCOM_TRIM 7 {"trim", EDITCOM_TRIM}, /* remove an event from an IP */ {NULL, 0}, }; bool slowedit(int opt, int argc, char **argv) { int ret, mode; EDITCOMMANDS *e; char *user; extern char *optarg; /* * Format of an edit command is "slowedit command switches IP * On entry to this routine, argv[0] is the command. */ mode = 0; for (e = EditCommands; e->token != NULL; ++e) { if (strcasecmp(argv[opt], e->token) == 0) { mode = e->mode; break; } } if (mode == 0) { printf("Usage: slowedit "); for (e = EditCommands; e->token != NULL; ++e) printf("%s%s", e == EditCommands ? "" : "|", e->token); printf("\n"); return FALSE; } ret = 0; user = cuserid(NULL); switch(mode) { /* add IP event */ case EDITCOM_ADD: ret = slowadd(user, opt, argc, argv); break; case EDITCOM_DELETE: ret = slowdelete(user, opt, argc, argv); break; case EDITCOM_LIST: ret = slowlist(user, opt, argc, argv); break; case EDITCOM_FIND: ret = slowfind(user, opt, argc, argv); break; case EDITCOM_ALIAS: ret = slowalias(user, opt, argc, argv); break; case EDITCOM_WHITE: ret = slowwhite(user, opt, argc, argv); break; case EDITCOM_TRIM: ret = slowtrim(user, opt, argc, argv); break; } return ret; } bool slowadd(char *user, int opt, int argc, char **argv) { char *iptext; char *event_str; char logprefix[BUFSIZ]; test_init(TRUE); ++opt; if ((argc - opt) < 2) { (void) printf("Usage: slowedit add IPnumber \"event,event,...\"\n"); show_events("Combine events from"); return FALSE; } iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); ++opt; event_str = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); if (slowsetpriv(iptext, event_str) == FALSE) return FALSE; if (user == NULL) sprintf(logprefix, "edit add: "); else sprintf(logprefix, "%s added: ", user); LogPrefix = logprefix; Priv->ip = inet_network(Priv->iptxt); if (push_to_db(&Ctx, Priv, FALSE) != SMFIS_CONTINUE) return FALSE; if (! isdebug(BUG_DELETE)) { (void) printf("slowedit: add %s by %s succeeded\n", iptext, user == NULL ? "noody" : user); } (void) free(event_str); return TRUE; } bool slowdelete(char *user, int opt, int argc, char **argv) { time_t t; char *iptext; PRIMARY_KEY ip; int ret; test_init(TRUE); ++opt; if ((argc - opt) < 1) { (void) printf("Usage: slowedit delete IPnumber [date]\n"); (void) printf("\tIf date missing, remove the IP record\n"); (void) printf("\telse remove just that event record.\n"); return FALSE; } iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); ip = inet_network(iptext); if ((int)ip == -1) { (void) printf("slowedit delete: %s: cannot interpret\n", iptext); return FALSE; } ++opt; if (argv[opt] == NULL) t = 0; else t = parse_date(argv[opt]); ret = del_ip_database(Dp1, Dp2, ip, t, FALSE); if (ret != 0) { (void) printf("slowedit: delete %s\n", ret == DB_NOTFOUND ? "Entry specified not in datbase": strerror(errno)); return FALSE; } # if HAVE_SYSLOG if (DoLog == TRUE && LogEvents == TRUE) syslog(LOG_INFO, "%s deleted %s %s\n", user == NULL ? Prog : user, iptext, t == 0 ? "" : argv[opt]); # endif (void) printf("slowedit: %s successfully deleted %s, event %s\n", user == NULL ? Prog : user, iptext, t == 0 ? "all-events" : argv[opt]); return TRUE; } bool slowlist(char *user, int opt, int argc, char **argv) { char *iptext; PRIMARY_KEY ip; PRIMARY_DATUM *dates, *d; SECONDARY_DATUM *event; SECONDARY_KEY k; int ndates; int i; test_init(TRUE); ++opt; if (argc <= opt) { printf("Usage: slowedit list [IPnumber | \"all\"]\n"); return FALSE; } iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); if (strcasecmp(iptext, "all") == 0) { if (dump_database(Dp1, Dp2, stdout) != 0) { milterr(Prog, __FILE__, __LINE__, FALSE, errno, iptext, "list all failed"); } return TRUE; } ip = inet_network(iptext); if ((int)ip == -1) { (void) printf("slowedit list: %s: cannot interpret\n", iptext); return FALSE; } ndates = 0; dates = get_ip_database(Dp1, ip, &ndates, FALSE); if (dates == NULL || ndates == 0) { (void) printf("slowedit: list %s %s\n", iptext, ndates == 0 ? "Entry specified not in datbase": strerror(errno)); return FALSE; } for (d = dates, i = 0; i < ndates; ++i, ++d) { k.ip = ip; k.date = *d; event = get_event_database(Dp2, &k, FALSE); if (event == NULL) { if (errno == DB_NOTFOUND) { printf("slowedit: list %s: no record found for date %s", iptext, ctime(d)); continue; } printf("slowedit: list %s: lookup error %d\n", iptext, errno); return FALSE; } (void)printf("%s\n", event_to_str(iptext, *d, event, FALSE)); } return TRUE; } bool slowfind(char *user, int opt, int argc, char **argv) { LOGICSTRUCT *logic; DBT key, data; SECONDARY_DATUM *event; SECONDARY_KEY k; DBC *curs; int ret; struct in_addr in; time_t *tp; char ipbuf[MAXHOSTNAMELEN]; char *cp; int i, records; BITMAP set; test_init(TRUE); ++opt; if (argc == opt) { (void) printf("Usage: slowedit find \"[and|or],relation,relation,...\"\n"); return FALSE; } cp = try_strdup(__FILE__, __LINE__, argv[opt], FALSE); logic = slowlogicparse(cp); if (logic == NULL) return FALSE; ret = Dp1->cursor(Dp1, NULL, &curs, 0); if (ret != 0) { Dp1->err(Dp1, ret, "%s\n", PrimaryDatabaseLoc); return ret; } (void) memset(&data, 0, sizeof data); (void) memset(&key, 0, sizeof key); key.flags = DB_DBT_REALLOC; data.flags = DB_DBT_REALLOC; for (;;) { ret = curs->c_get(curs, &key, &data, DB_NEXT); if (ret == DB_NOTFOUND) break; if (ret != 0) { Dp1->err(Dp1, ret, "%s\n", PrimaryDatabaseLoc); return ret; } if (key.data == NULL) { continue; } records = data.size / sizeof(PRIMARY_DATUM); (void) memcpy(&in.s_addr, ((char *)(key.data)), sizeof(in.s_addr)); in.s_addr = htonl(in.s_addr); (void) sprintf(ipbuf, "%s", inet_ntoa(in)); tp = (time_t *)data.data; if (records != 0 && *tp < MAX_TYPE_RECORD) { /* WHITELIST ALIAS */ } for (i = 0; i < records; i++, tp++) { k.ip = *((PRIMARY_KEY *)(key.data)); k.date = *tp; event = get_event_database(Dp2, &k, FALSE); if (event == NULL) continue; if (logic->andor == SLOWBLOGICAND) { /* * Since logic AND means everything must match * we continue (skip printing) on any mismatch. */ switch(logic->envrcpts.relation) { case SLOWLOGICEQUAL: if (event->envrcpts != logic->envrcpts.value) continue; break; case SLOWLOGICGREATER: if (event->envrcpts <= logic->envrcpts.value) continue; break; case SLOWLOGICLESSTHAN: if (event->envrcpts >= logic->envrcpts.value) continue; break; case SLOWLOGICNONE: break; default: continue; } switch(logic->hdrrcpts.relation) { case SLOWLOGICEQUAL: if (event->headrcpts != logic->hdrrcpts.value) continue; break; case SLOWLOGICGREATER: if (event->headrcpts <= logic->hdrrcpts.value) continue; break; case SLOWLOGICLESSTHAN: if (event->headrcpts >= logic->hdrrcpts.value) continue; break; case SLOWLOGICNONE: break; default: continue; } switch(logic->honeyrcpts.relation) { case SLOWLOGICEQUAL: if (event->honeyrcpts != logic->honeyrcpts.value) continue; break; case SLOWLOGICGREATER: if (event->honeyrcpts <= logic->honeyrcpts.value) continue; break; case SLOWLOGICLESSTHAN: if (event->honeyrcpts >= logic->honeyrcpts.value) continue; break; case SLOWLOGICNONE: break; default: continue; } switch(logic->bad.relation) { case SLOWLOGICEQUAL: if (event->badrcpts != logic->bad.value) continue; break; case SLOWLOGICGREATER: if (event->badrcpts <= logic->bad.value) continue; break; case SLOWLOGICLESSTHAN: if (event->badrcpts >= logic->bad.value) continue; break; case SLOWLOGICNONE: break; default: continue; } if (logic->bits[SLOWSETBITS] != 0) { /* * The specified bits must be set */ set = logic->bits[SLOWSETBITS] & event->eventmap; if (set == 0) continue; } if (logic->bits[SLOWONLYBITS] != 0) { /* * The specified bits must be the only ones set */ set = logic->bits[SLOWONLYBITS] - event->eventmap; if (set != 0) continue; } if (logic->bits[SLOWCLEARBITS] != 0) { /* * The specified bits must be clear (0) */ set = logic->bits[SLOWCLEARBITS] & ~(event->eventmap); if (set == 0) continue; } if (logic->reg != NULL && event->msgid[0] != '\0') { # if HAVE_REGEX_H && HAVE_REGCOMP regmatch_t pmatch; int rerr; rerr = regexec(logic->reg, event->msgid, 1, &pmatch, 0); if (rerr != 0) continue; # else if (strstr(event->msgid, logic->reg) != 0) continue; # endif } else if (logic->reg != NULL) continue; (void) printf("%s\n", event_to_str(ipbuf, k.date, event, FALSE)); continue; } /* * Since logic OR means anythin may match * jump to print on any match. */ switch(logic->envrcpts.relation) { case SLOWLOGICEQUAL: if (event->envrcpts == logic->envrcpts.value) goto print; break; case SLOWLOGICGREATER: if (event->envrcpts > logic->envrcpts.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->envrcpts < logic->envrcpts.value) goto print; break; } switch(logic->hdrrcpts.relation) { case SLOWLOGICEQUAL: if (event->headrcpts == logic->hdrrcpts.value) goto print; break; case SLOWLOGICGREATER: if (event->headrcpts > logic->hdrrcpts.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->headrcpts < logic->hdrrcpts.value) goto print; break; } switch(logic->honeyrcpts.relation) { case SLOWLOGICEQUAL: if (event->honeyrcpts == logic->honeyrcpts.value) goto print; break; case SLOWLOGICGREATER: if (event->honeyrcpts > logic->honeyrcpts.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->honeyrcpts < logic->honeyrcpts.value) goto print; break; } switch(logic->bad.relation) { case SLOWLOGICEQUAL: if (event->badrcpts == logic->bad.value) goto print; break; case SLOWLOGICGREATER: if (event->badrcpts > logic->bad.value) goto print; break; case SLOWLOGICLESSTHAN: if (event->badrcpts < logic->bad.value) goto print; break; } if (logic->bits[SLOWSETBITS] != 0) { /* * The specified bits must be set */ set = logic->bits[SLOWSETBITS] & event->eventmap; if (set != 0) goto print; } if (logic->bits[SLOWONLYBITS] != 0) { /* * The specified bits must be the only ones set */ set = logic->bits[SLOWONLYBITS] - event->eventmap; if (set == 0) goto print; } if (logic->bits[SLOWCLEARBITS] != 0) { /* * The specified bits must be clear (0) */ set = logic->bits[SLOWCLEARBITS] & ~(event->eventmap); if (set != 0) goto print; } if (logic->reg != NULL && event->msgid[0] != '\0') { # if HAVE_REGEX_H && HAVE_REGCOMP regmatch_t pmatch; if (regexec(logic->reg, event->msgid, 1, &pmatch, 0) == 0) # else if (strstr(event->msgid, logic->reg) == 0) # endif goto print; } continue; print: (void) printf("%s\n", event_to_str(ipbuf, k.date, event, FALSE)); continue; } } (void) curs->c_close(curs); return TRUE; } bool slowalias(char *user, int opt, int argc, char **argv) { test_init(TRUE); printf("Usage: slowedit alias IPexisting IPnew\n"); return TRUE; } bool slowwhite(char *user, int opt, int argc, char **argv) { test_init(TRUE); printf("Usage: slowedit white IPnumber why\n"); return TRUE; } bool slowtrim(char *user, int opt, int argc, char **argv) { test_init(TRUE); printf("Usage: slowedit trim [-o oldest] [-n newest] [-r range] \n"); return TRUE; } bool slowgarbage(char *user, int opt, int argc, char **argv) { test_init(TRUE); printf("Usage: slowedit garbage\n"); return TRUE; } bool slowrebuild(char *user, int opt, int argc, char **argv) { test_init(TRUE); printf("Usage: slowedit rebuild [-o outputfile] [-i inputfile]\n"); return TRUE; } typedef struct { char *event; int index; } EVENT_LIST; EVENT_LIST EventList[] = { # define EVENT_ENVRCPTS 1 {"er", EVENT_ENVRCPTS}, /* envelope recipients */ # define EVENT_HDRRCPTS 2 {"hr", EVENT_HDRRCPTS}, /* header recipients */ # define EVENT_HONEYRCPTS 3 {"ho", EVENT_HONEYRCPTS}, /* honey pot recipients */ # define EVENT_BADRCPTS 4 {"br", EVENT_BADRCPTS}, /* bad envelope recipients */ # define EVENT_TOOHDR 5 {"hed", EVENT_TOOHDR}, /* too many header recipients */ # define EVENT_TOOENV 6 {"env", EVENT_TOOENV}, /* too many envelope recipients */ # define EVENT_HONEY 7 {"hon", EVENT_HONEY}, /* one or more honey pots rcpts */ # define EVENT_MSGID 8 {"mid", EVENT_MSGID}, /* bad or missing message-id header */ # define EVENT_FROMH 9 {"frm", EVENT_FROMH}, /* bad from header */ # define EVENT_BADHO 10 {"bho", EVENT_BADHO}, /* bad envelope sender host */ # define EVENT_RBL 11 {"rbl", EVENT_RBL}, /* reported bad by an RBL host */ # define EVENT_MILT 12 {"abo", EVENT_MILT}, /* The milter aborted */ # define EVENT_ADVW 13 {"pip", EVENT_ADVW}, /* Attempt at an advanced write */ # define EVENT_FORGE 14 {"for", EVENT_FORGE}, /* sendmail reported this as forged */ # define EVENT_FROMMX 15 {"mmx", EVENT_FROMMX}, /* received from one of our MX servers */ # define EVENT_NOTINET 16 {"net", EVENT_NOTINET}, /* not received via a TCP/IP network socket */ # define EVENT_MESGID 17 {"msgid", EVENT_MESGID}, /* The text that is the message-id header */ {NULL, 0}, }; void show_events(char *str) { EVENT_LIST *e; int count; printf("%s:\n\t", str); count = 0; for (e = EventList; e->event != NULL; ++e) { if (++count == 6) { printf("\n\t"); count = 0; } else if (e != EventList) printf(", "); if (strlen(e->event) == 2) printf("%s=#", e->event); else printf("%s", e->event); } printf("\n"); } int is_event(char *str) { EVENT_LIST *e; for (e = EventList; e->event != NULL; ++e) { if (strcasecmp(e->event, str) == 0) return e->index; } return 0; } bool slowsetpriv(char *iptext, char *estr) { char **list, **l; char **equate; char *item; int value; int index; list = mkargv(estr, ',', FALSE); equate = NULL; if (list == NULL) { (void) printf("slowedit: events missing\n"); return FALSE; } if (isdebug(BUG_EDIT)) print_list("slowsetpriv", list, stdout); for (l = list; *l != NULL; l++) { value = -1; if (strchr(*l, '=') != NULL) { equate = mkargv(*l, '=', FALSE); if (equate == NULL) continue; item = try_strdup(__FILE__, __LINE__, equate[0], FALSE); if (equate[1] != NULL) value = atoi(equate[1]); else value = 0; } else item = try_strdup(__FILE__, __LINE__, *l, FALSE); index = is_event(item); if (index == 0) { (void) printf("%s: Unrecognized event keyword\n", item); show_events("Combine events from"); return FALSE; } if (strlen(item) == 3 && value >= 0) { (void) printf("slowedit: %s: Three letter events are boolean.", item); (void) printf(" Do not use %s=value form.\n", item); return FALSE; } switch(index) { /* count of envelope recipients */ case EVENT_ENVRCPTS: if (value <= 0) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not have a value zero or less"); } Priv->envrcpts = value; break; case EVENT_HDRRCPTS: if (value <= 0) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not have a value zero or less"); } Priv->nhdrrcpts = value; break; case EVENT_HONEYRCPTS: if (value <= 0) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not have a value zero or less"); } Priv->honeyrcpts = value; break; case EVENT_BADRCPTS: if (value <= 0) { milterr(Prog, __FILE__, __LINE__, FALSE, EINVAL, item, "May not have a value zero or less"); } Priv->badrcpts = value; break; case EVENT_TOOHDR: set_bit(BIT_TOO_MANY_HDR_RCPTS, Priv->bits); break; case EVENT_TOOENV: set_bit(BIT_TOO_MANY_ENV_RCPTS, Priv->bits); break; case EVENT_HONEY: set_bit(BIT_GOT_HONEYPOT, Priv->bits); break; case EVENT_MSGID: set_bit(BIT_BAD_MSG_ID, Priv->bits); break; case EVENT_FROMH: set_bit(BIT_BAD_FROM_HEAD, Priv->bits); break; case EVENT_BADHO: set_bit(BIT_BAD_HOST, Priv->bits); break; case EVENT_RBL: set_bit(BIT_RBL_BAD, Priv->bits); break; case EVENT_MILT: set_bit(BIT_MILTER_ABORTED, Priv->bits); break; case EVENT_ADVW: set_bit(BIT_ADVANCEWRITE, Priv->bits); break; case EVENT_FORGE: set_bit(BIT_FORGED, Priv->bits); break; case EVENT_FROMMX: set_bit(BIT_FROMMX, Priv->bits); break; case EVENT_NOTINET: set_bit(BIT_NOTINET, Priv->bits); break; case EVENT_MESGID: if (equate != NULL && equate[1] != NULL) Priv->midstr = try_strdup(__FILE__, __LINE__, equate[1], FALSE); break; } (void) free(item); } Priv->iptxt = try_strdup(__FILE__, __LINE__, iptext, FALSE); return TRUE; } LOGICSTRUCT * slowlogicparse(char *estr) { char **list, **l; char **equate; char *item; int val; int index; int ch, rel, whichbits; LOGICSTRUCT *logic; list = mkargv(estr, ',', FALSE); if (list == NULL) { (void) printf("slowedit: events missing\n"); return FALSE; } if (isdebug(BUG_EDIT)) print_list("slowsetpriv", list, stdout); logic = (LOGICSTRUCT *)try_alloc(__FILE__, __LINE__, sizeof(*logic), FALSE); if (logic == NULL) return NULL; (void)memset((char *)logic, '\0', sizeof(*logic)); logic->andor = SLOWBLOGICAND; /* default to AND */ for (l = list; *l != NULL; l++) { if (strcasecmp(*l, "and") == 0) { logic->andor = SLOWBLOGICAND; continue; } if (strcasecmp(*l, "or") == 0) { logic->andor = SLOWBLOGICOR; continue; } val = -1; rel = SLOWLOGICNONE; whichbits = SLOWSETBITS; if (strchr(*l, '=') != NULL) { rel = SLOWLOGICEQUAL; ch = '='; } else if (strchr(*l, '>') != NULL) { rel = SLOWLOGICGREATER; ch = '>'; } else if (strchr(*l, '<') != NULL) { rel = SLOWLOGICLESSTHAN; ch = '<'; } else { (void) printf("%s: Unrecognized or missing comparitor\n", *l); (void) printf("\tselect from: = (equal to), < (less than), or > (greater than\n"); exit (EINVAL); } /* * Other than "and" and "or", everyting is in the format * keyword [<>=] value, * For non-bitmap items, value is numeric. * For bitmap items, value must be =[set,only,clear] * For msgid value must be ="regular expression" */ equate = mkargv(*l, ch, FALSE); if (equate == NULL) continue; if (equate[1] == NULL) { (void) printf("%s: Value portion missing from relationshipe.\n", *l); exit(EINVAL); } item = try_strdup(__FILE__, __LINE__, equate[0], FALSE); index = is_event(item); if (index == 0) { (void) printf("%s: Unrecognized event keyword\n", item); show_events("Combine events from"); return FALSE; } if (isdigit((int)(equate[1][0]))) { val = atoi(equate[1]); whichbits = SLOWSETBITS; } else { if (index == EVENT_MESGID) { # if HAVE_REGEX_H && HAVE_REGCOMP int rerr; logic->reg = try_alloc(__FILE__, __LINE__, sizeof(regex_t), FALSE); memset((char *)(logic->reg), '\0', sizeof(regex_t)); rerr = regcomp(logic->reg, equate[1], REG_EXTENDED|REG_ICASE); if (rerr != 0) { char rerrbuf[BUFSIZ]; (void) regerror(rerr, logic->reg, rerrbuf, BUFSIZ); (void) printf("%s: %s\n", item, rerrbuf); exit (rerr); } # else logic->reg = try_strdup(__FILE__, __LINE__, equate[1], FALSE); # endif continue; } val = 0; if (strcasecmp(equate[1], "set") == 0) whichbits = SLOWSETBITS; else if (strcasecmp(equate[1], "only") == 0) whichbits = SLOWONLYBITS; else if (strcasecmp(equate[1], "clear") == 0) whichbits = SLOWCLEARBITS; else { (void) printf("%s: Unrecognized bit keyword\n", item); (void) printf("\tselect from: set, only, or clear\n"); exit (EINVAL); } } switch(index) { case EVENT_ENVRCPTS: logic->envrcpts.relation = rel; logic->envrcpts.value = val; break; case EVENT_HDRRCPTS: logic->hdrrcpts.relation = rel; logic->hdrrcpts.value = val; break; case EVENT_HONEYRCPTS: logic->honeyrcpts.relation = rel; logic->honeyrcpts.value = val; break; case EVENT_BADRCPTS: logic->bad.relation = rel; logic->bad.value = val; break; case EVENT_TOOHDR: set_bit(BIT_TOO_MANY_HDR_RCPTS, logic->bits[whichbits]); break; case EVENT_TOOENV: set_bit(BIT_TOO_MANY_ENV_RCPTS, logic->bits[whichbits]); break; case EVENT_HONEY: set_bit(BIT_GOT_HONEYPOT, logic->bits[whichbits]); break; case EVENT_MSGID: set_bit(BIT_BAD_MSG_ID, logic->bits[whichbits]); break; case EVENT_FROMH: set_bit(BIT_BAD_FROM_HEAD, logic->bits[whichbits]); break; case EVENT_BADHO: set_bit(BIT_BAD_HOST, logic->bits[whichbits]); break; case EVENT_RBL: set_bit(BIT_RBL_BAD, logic->bits[whichbits]); break; case EVENT_MILT: set_bit(BIT_MILTER_ABORTED, logic->bits[whichbits]); break; case EVENT_ADVW: set_bit(BIT_ADVANCEWRITE, logic->bits[whichbits]); break; case EVENT_FORGE: set_bit(BIT_FORGED, logic->bits[whichbits]); break; case EVENT_FROMMX: set_bit(BIT_FROMMX, logic->bits[whichbits]); break; case EVENT_NOTINET: set_bit(BIT_NOTINET, logic->bits[whichbits]); break; } (void) free(item); } return logic; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 4222 | bryan_costales |
Massive rewrite to speed up the database writes. Using a single database now with duplicate keys where the keys are the IP numbers. Added a purge command and removed the garbage command. Fixed some leaking memory bugs and properly closed the database in a few places were it was not properly closed. Updated the docs to reflect this and bumped both the database version and release number. Running on a FreeBSD 3.x machine and a Solaris 9 machine. |
||
#5 | 4052 | bryan_costales |
Implimented: whitelisting AddMXHost for MX servers that lie Converted to thread safe DNS routines garbage collection RunAsUser and RunAsGroup for root startups rebuild the database summarize by IP number Finished all documentation. Moved release from alpha to beta |
||
#4 | 4030 | bryan_costales |
Finished documenting the configuration file Fixed a race condition and a core dump bug. Added hooks for whitelisting and IP aliasing Added support for Berkeley DB 4.2 Converted to htonl() and ntohl() Known Bugs: ip.db cannot be shared over NFS IP tracking from MX hosts can fail A RunAsUser config option is needed. |
||
#3 | 3998 | bryan_costales |
Brought the whole distribution up to V0.9 Added a huge abount of documentation. Added slowedit find Created startup scripts to launch for testing Fixed numerous bugs. Fixed a few portablity issues. Installed hooks for whitelisting and IP aliases. |
||
#2 | 3957 | bryan_costales |
Added rbl lookup support and testing for same. Folded in support for smfi_stop(). Added lots of slowedit commands Fixed a serious bug in MX lookups. Added to documentation. |
||
#1 | 3890 | bryan_costales |
This is the 0.6 release. The following have been added with the uses indicated: Source files: edit.c -- the slowedit functions compat.c -- missing system files Autoconf: configure.ac, makefile.am config.h aclocal.m4 acinclude.m4 build/ Documents: doc/ -- html and man(1) documents Testing: tests/ -- regressive testing TODO: -- revised to show actual progress |