Exemple #1
0
sfsistat mlfi_eom(SMFICTX * ctx)
{
	struct mlfiPriv *priv = GETCONTEXT(ctx);

	if (config.stamp == 0)
		return mlfi_cleanup(ctx);

	switch (priv->stamp) {
	case STAMP_PASSED:
		smfi_addheader(ctx, "X-DNSBL-MILTER", "Passed");
		break;
	case STAMP_WHITELISTED:
		smfi_addheader(ctx, "X-DNSBL-MILTER", "Whitelisted");
		break;
	case STAMP_SKIPPED:
		smfi_addheader(ctx, "X-DNSBL-MILTER", "Skipped");
		break;
	case STAMP_BLACKLISTED:
		smfi_addheader(ctx, "X-DNSBL-MILTER", "Blacklisted");
		break;
	default:
		smfi_addheader(ctx, "X-DNSBL-MILTER", "Unknown error");
		break;
	}

	return mlfi_cleanup(ctx);
}
Exemple #2
0
int
msgmod_add_header(void *ctx, int argc, var_t *args[])
{
	char *headerf;
	char *headerv;

	if (argc != 2)
	{
		log_error("msgmod_add_header: bad argument count");
		return -1;
	}

	headerf = args[0]->v_data;
	headerv = args[1]->v_data;

	if (smfi_addheader(ctx, headerf, headerv) != MI_SUCCESS)
	{
		log_error("msgmod_add_header: smfi_addheader failed");
		return -1;
	}

	log_debug("msgmod_add_header: %s: %s", headerf, headerv);

	return 0;
}
Exemple #3
0
	/*}}}*/
}	priv_t;
static char *
xfree (char *s) /*{{{*/
{
	if (s)
		free (s);
	return NULL;
}/*}}}*/
static bool_t
xcopy (char **buf, const char *str) /*{{{*/
{
	if (*buf)
		free (*buf);
	*buf = str ? strdup (str) : NULL;
	return (! str) || *buf ? true : false;
}/*}}}*/
static void
priv_clear (priv_t *p) /*{{{*/
{
	if (p) {
		p -> x_agn = 0;
		p -> from = xfree (p -> from);
		p -> receiver = charc_free_all (p -> receiver);
		p -> prev = NULL;
		p -> info = xfree (p -> info);
	}
}/*}}}*/
static priv_t *
priv_free (priv_t *p) /*{{{*/
{
	if (p) {
		priv_clear (p);
		if (p -> lg)
			log_free (p -> lg);
		if (p -> cfg)
			cfg_free (p -> cfg);
		free (p);
	}
	return NULL;
}/*}}}*/
static priv_t *
priv_alloc (void) /*{{{*/
{
	priv_t	*p;
	
	if (p = (priv_t *) malloc (sizeof (priv_t)))
		if (p -> cfg = cfg_alloc (cfgfile)) {
			p -> is_local = false;
			p -> x_agn = 0;
			p -> from = NULL;
			p -> receiver = NULL;
			p -> prev = NULL;
			p -> info = NULL;
			if (! (p -> lg = log_alloc (NULL, program, loglevel)))
				p = priv_free (p);
		} else {
			free (p);
			p = NULL;
		}
	return p;
}/*}}}*/
static bool_t
priv_setfrom (priv_t *p, const char *from) /*{{{*/
{
	return xcopy (& p -> from, from);
}/*}}}*/
static bool_t
priv_setto (priv_t *p, const char *to) /*{{{*/
{
	charc_t	*r;
	
	if (r = charc_alloc (to)) {
		if (p -> prev)
			p -> prev -> next = r;
		else
			p -> receiver = r;
		p -> prev = r;
	}
	return r ? true : false;
}/*}}}*/
static bool_t
priv_addinfo (priv_t *p, const char *info) /*{{{*/
{
	char	*temp;

	if ((! p -> info) || (! p -> info[0]))
		return xcopy (& p -> info, info);
	if (temp = malloc (strlen (p -> info) + strlen (info) + 2)) {
		sprintf (temp, "%s,%s", p -> info, info);
		free (p -> info);
		p -> info = temp;
		return true;
	}
	return false;
}/*}}}*/
static bool_t
priv_addinfopair (priv_t *p, const char *var, const char *val) /*{{{*/
{
	bool_t	rc;
	char	*scratch, *ptr;
	
	if (scratch = malloc (strlen (var) + strlen (val) + 2)) {
		for (ptr = scratch; *var; *ptr++ = *var++)
			;
		*ptr++ = '=';
		for (;*val; ++val)
			*ptr++ = *val == ',' ? '_' : *val;
		*ptr = '\0';
		rc = priv_addinfo (p, scratch);
		free (scratch);
	} else
		rc = false;
	return rc;
}/*}}}*/

static sfsistat
handle_connect (SMFICTX *ctx, char  *hostname, _SOCK_ADDR *hostaddr) /*{{{*/
{
	priv_t	*p;

	if (! (p = priv_alloc ()))
		return SMFIS_TEMPFAIL;
	if (hostaddr -> sa_family == AF_INET) {
		struct sockaddr_in	*iaddr = (struct sockaddr_in *) hostaddr;

		if (ntohl (iaddr -> sin_addr.s_addr) == INADDR_LOOPBACK)
			p -> is_local = true;
	}
# ifdef		AF_INET6
	else if (hostaddr -> sa_family == AF_INET6) {
		struct sockaddr_in6	*i6addr = (struct sockaddr_in6 *) hostaddr;
		static struct in6_addr	loopback = IN6ADDR_LOOPBACK_INIT;
		
		if (memcmp (& i6addr -> sin6_addr, & loopback, sizeof (i6addr -> sin6_addr)) == 0)
			p -> is_local = true;
	}
# endif		/* AF_INET6 */
	smfi_setpriv (ctx, p);
	return SMFIS_CONTINUE;
}/*}}}*/
static sfsistat
handle_from (SMFICTX *ctx, char **argv) /*{{{*/
{
	priv_t	*p = (priv_t *) smfi_getpriv (ctx);
	
	if (! p)
		return SMFIS_TEMPFAIL;
	priv_clear (p);
	if (! priv_setfrom (p, argv[0]))
		return SMFIS_TEMPFAIL;
	priv_addinfopair (p, "from", argv[0]);
	return SMFIS_CONTINUE;
}/*}}}*/
static sfsistat
handle_to (SMFICTX *ctx, char **argv) /*{{{*/
{
	priv_t	*p = (priv_t *) smfi_getpriv (ctx);
	char	*chk, *opt;
	bool_t	reject, tempfail;
	
	if (! p)
		return SMFIS_TEMPFAIL;
	if (p -> is_local)
		return SMFIS_CONTINUE;
	if (! (chk = cfg_valid_address (p -> cfg, argv[0]))) {
		log_out (p -> lg, LV_ERROR, "Unable to setup initial data for `%s'", argv[0]);
		return SMFIS_TEMPFAIL;
	}
	if (opt = strchr (chk, ':'))
		*opt++ = '\0';
	reject = false;
	tempfail = false;
	if (! strcmp (chk, ID_REJECT))
		reject = true;
	else if (! strcmp (chk, ID_TEMPFAIL))
		tempfail = true;
	else if ((! strcmp (chk, ID_ACCEPT)) && opt)
		priv_addinfo (p, opt);
	priv_addinfopair (p, "to", argv[0]);
	free (chk);
	if (reject) {
		log_out (p -> lg, LV_INFO, "Receiver `%s' is rejected", argv[0]);
		smfi_setreply (ctx, (char *) "550", (char *) "5.1.1", (char *) "No such user");
		return SMFIS_REJECT;
	}
	if (tempfail) {
		log_out (p -> lg, LV_INFO, "Receiver `%s' is temp. disbaled", argv[0]);
		smfi_setreply (ctx, (char *) "400", (char *) "4.0.0", (char *) "Please try again later");
		return SMFIS_TEMPFAIL;
	}
	if (! priv_setto (p, argv[0]))
		return SMFIS_TEMPFAIL;
	return SMFIS_CONTINUE;
}/*}}}*/
static sfsistat
handle_header (SMFICTX *ctx, char *field, char *value) /*{{{*/
{
	priv_t	*p = (priv_t *) smfi_getpriv (ctx);

	if (! p)
		return SMFIS_TEMPFAIL;
	if (p -> is_local)
		return SMFIS_CONTINUE;
	if (! strcasecmp (field, X_LOOP)) {
		log_out (p -> lg, LV_WARNING, "Mail from `%s' has already loop marker set, rejected", p -> from);
		smfi_setreply (ctx, (char *) "500", (char *) "5.4.6", (char *) "Loop detected");
		return SMFIS_REJECT;
	}
	if (! strcasecmp (field, X_AGN))
		p -> x_agn++;
	return SMFIS_CONTINUE;
}/*}}}*/
static sfsistat
handle_eom (SMFICTX *ctx) /*{{{*/
{
	priv_t	*p = (priv_t *) smfi_getpriv (ctx);
	int	n;
	
	if (! p)
		return SMFIS_TEMPFAIL;
	for (n = 0; n < p -> x_agn; ++n)
		smfi_chgheader (ctx, (char *) X_AGN, 0, NULL);
	if (! p -> is_local) {
		if (p -> info)
			smfi_addheader (ctx, (char *) X_AGN, p -> info);
		smfi_addheader (ctx, (char *) X_LOOP, (char *) LOOP_SET);
	}
	return SMFIS_CONTINUE;
}/*}}}*/
Exemple #4
0
static void add_x_header(SMFICTX *ctx, char *st, unsigned int scanned, unsigned int status) {
    if(addxvirus == 1) { /* Replace/Yes */
	while(scanned)
	    if(smfi_chgheader(ctx, (char *)"X-Virus-Scanned", scanned--, NULL) != MI_SUCCESS)
		logg("^Failed to remove existing X-Virus-Scanned header\n");
	while(status)
	    if(smfi_chgheader(ctx, (char *)"X-Virus-Status", status--, NULL) != MI_SUCCESS)
		logg("^Failed to remove existing X-Virus-Status header\n");
	if(smfi_addheader(ctx, (char *)"X-Virus-Scanned", xvirushdr) != MI_SUCCESS)
	    logg("^Failed to add X-Virus-Scanned header\n");
	if(smfi_addheader(ctx, (char *)"X-Virus-Status", st) != MI_SUCCESS)
	    logg("^Failed to add X-Virus-Status header\n");
    } else { /* Add */
	if(smfi_insheader(ctx, 1, (char *)"X-Virus-Scanned", xvirushdr) != MI_SUCCESS)
	    logg("^Failed to insert X-Virus-Scanned header\n");
	if(smfi_insheader(ctx, 1, (char *)"X-Virus-Status", st) != MI_SUCCESS)
	    logg("^Failed to insert X-Virus-Status header\n");
    }
}
static sfsistat
cb_eom(SMFICTX *ctx)
{
    struct context *context;
    char sclstr[16];

    if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
        msg(LOG_ERR, NULL, "cb_eom: smfi_getpriv");
        return (SMFIS_ACCEPT);
    }
    msg(LOG_DEBUG, context, "cb_eom()");

    if (context->sclvalue != -1) {
        snprintf(sclstr, 16, "%d", context->sclvalue);
        if (smfi_addheader(ctx, "X-MS-Exchange-Organization-SCL", sclstr) != MI_SUCCESS)
            msg(LOG_ERR, context, "cb_eom: smfi_addheader");
    }
    return (SMFIS_ACCEPT);
}
Exemple #6
0
int 
spamdscan(SMFICTX *ctx, struct mlfi_priv *priv, struct config_file *cfg, char **subject, int extra)
{
	int retry, r = -2, hr = 0, to_trace = 0;
	struct timeval t;
	double ts, tf;
	struct spamd_server *selected = NULL;
	char rbuf[BUFSIZ], hdrbuf[BUFSIZ];
	char *prefix = "s", *mid = NULL, *c;
	rspamd_result_t res;
	struct rspamd_metric_result *cur = NULL, *tmp;
	struct rspamd_symbol *cur_symbol, *tmp_symbol;
	enum rspamd_metric_action res_action = METRIC_ACTION_NOACTION;
	struct timespec sleep_ts;
	

	gettimeofday(&t, NULL);
	ts = t.tv_sec + t.tv_usec / 1000000.0;
	retry = cfg->spamd_retry_count;
	sleep_ts.tv_sec = cfg->spamd_retry_timeout / 1000;
	sleep_ts.tv_nsec = (cfg->spamd_retry_timeout % 1000) * 1000000ULL;

	TAILQ_INIT(&res);

	/* try to scan with available servers */
	while (1) {
		if (extra) {
			selected = (struct spamd_server *) get_random_upstream ((void *)cfg->extra_spamd_servers,
					cfg->extra_spamd_servers_num, sizeof (struct spamd_server),
					t.tv_sec, cfg->spamd_error_time, cfg->spamd_dead_time, cfg->spamd_maxerrors);
		}
		else {
			selected = (struct spamd_server *) get_random_upstream ((void *)cfg->spamd_servers,
					cfg->spamd_servers_num, sizeof (struct spamd_server),
					t.tv_sec, cfg->spamd_error_time, cfg->spamd_dead_time, cfg->spamd_maxerrors);
		}
		if (selected == NULL) {
			msg_err ("spamdscan: upstream get error, %s", priv->file);
			return -1;
		}
		
		if (selected->type == SPAMD_SPAMASSASSIN) {
			prefix = "s";
			r = spamdscan_socket (priv->file, selected, cfg, &res);
		}
		else {
			prefix = "rs";
			r = rspamdscan_socket (ctx, priv, selected, cfg, &res, &mid);
		}
		if (r == 0 || r == 1) {
			upstream_ok (&selected->up, t.tv_sec);
			break;
		}
		upstream_fail (&selected->up, t.tv_sec);
		if (r == -2) {
			msg_warn("%spamdscan: unexpected problem, %s, %s", prefix, selected->name, priv->file);
			break;
		}
		if (--retry < 1) {
			msg_warn("%spamdscan: retry limit exceeded, %s, %s", prefix, selected->name, priv->file);
			break;
		}

		msg_warn("%spamdscan: failed to scan, retry, %s, %s", prefix, selected->name, priv->file);
		nanosleep (&sleep_ts, NULL);
	}

	/*
	 * print scanning time, server and result
	 */
	gettimeofday(&t, NULL);
	tf = t.tv_sec + t.tv_usec / 1000000.0;
	
	/* Parse res tailq */
	cur = TAILQ_FIRST(&res);
	while (cur) {
		if (cur->metric_name) {
			if (cfg->extended_spam_headers) {
				hr = snprintf (hdrbuf, sizeof (hdrbuf), "%s: %s [%.2f / %.2f]%c",
						cur->metric_name,
						cur->score > cur->required_score ? "True" : "False",
						cur->score,
						cur->required_score,
						TAILQ_FIRST(&cur->symbols) != NULL ? '\n' : ' ');
			}
			r = snprintf (rbuf, sizeof (rbuf), "spamdscan: scan qid: <%s>, mid: <%s>, %f, %s, metric: %s: [%f / %f], symbols: ",
					priv->mlfi_id,
					(mid != NULL) ? mid : "undef",
					tf - ts,
					selected->name,
					cur->metric_name,
					cur->score,
					cur->required_score);
			free (cur->metric_name);
		}
		else {
			if (cfg->extended_spam_headers) {
				hr = snprintf (hdrbuf, sizeof (hdrbuf), "%s: %s [%.2f / %.2f]%c",
						"default",
						cur->score > cur->required_score ? "True" : "False",
						cur->score,
						cur->required_score,
						TAILQ_FIRST(&cur->symbols) != NULL ? '\n' : ' ');
			}
			r = snprintf (rbuf, sizeof (rbuf), "spamdscan: scan <%s>, %f, %s, metric: default: [%f / %f], symbols: ",
					priv->mlfi_id,
					tf - ts,
					selected->name,
					cur->score,
					cur->required_score);
		
		}
		if (cur->action > res_action) {
			res_action = cur->action;
			if (res_action == METRIC_ACTION_REWRITE_SUBJECT && cur->subject != NULL) {
				/* Copy subject as it would be freed further */
				if (*subject != NULL) {
					free (*subject);
				}
				*subject = strdup (cur->subject);
			}
		}
		/* Write symbols */
		cur_symbol = TAILQ_FIRST(&cur->symbols);
		if (cur_symbol == NULL) {
			r += snprintf (rbuf + r, sizeof (rbuf) - r, "no symbols");
		}
		else {
			while (cur_symbol) {
				if (cur_symbol->symbol) {
					if (TAILQ_NEXT (cur_symbol, entry)) {
						r += snprintf (rbuf + r, sizeof (rbuf) - r, "%s, ", cur_symbol->symbol);
					}
					else {
						r += snprintf (rbuf + r, sizeof (rbuf) - r, "%s", cur_symbol->symbol);
					}
					if (cfg->trace_symbol) {
						c = strchr (cur_symbol->symbol, '(');
						if (c != NULL) {
							*c = '\0';
						}
						if ( !strcmp (cfg->trace_symbol, cur_symbol->symbol)) {
							to_trace ++;
						}
					}
					if (cfg->extended_spam_headers) {
						if (TAILQ_NEXT (cur_symbol, entry)) {
							hr += snprintf (hdrbuf + hr, sizeof (hdrbuf) - hr, " %s\n",
									cur_symbol->symbol);
						}
						else {
							hr += snprintf (hdrbuf + hr, sizeof (hdrbuf) - hr, " %s",
									cur_symbol->symbol);
						}
					}
					free (cur_symbol->symbol);
				}
				tmp_symbol = cur_symbol;
				cur_symbol = TAILQ_NEXT(cur_symbol, entry);
				free (tmp_symbol);
			}
		}
		msg_info ("%s", rbuf);
		if (cur->subject != NULL) {
			free (cur->subject);
		}

		tmp = cur;
		cur = TAILQ_NEXT(cur, entry);

		free (tmp);
		if (cfg->extended_spam_headers) {
			if (extra) {
				smfi_addheader (ctx, "X-Spamd-Extra-Result", hdrbuf);
			}
			else {
				smfi_addheader (ctx, "X-Spamd-Result", hdrbuf);
			}
		}
	}
	/* All other statistic headers */
	if (cfg->extended_spam_headers) {
		if (extra) {
			smfi_addheader (ctx, "X-Spamd-Extra-Server", selected->name);
			snprintf (hdrbuf, sizeof (hdrbuf), "%.2f", tf - ts);
			smfi_addheader (ctx, "X-Spamd-Extra-Scan-Time", hdrbuf);
		}
		else {
			smfi_addheader (ctx, "X-Spamd-Server", selected->name);
			snprintf (hdrbuf, sizeof (hdrbuf), "%.2f", tf - ts);
			smfi_addheader (ctx, "X-Spamd-Scan-Time", hdrbuf);
			smfi_addheader (ctx, "X-Spamd-Queue-ID", priv->mlfi_id);
		}
	}
	/* Trace spam messages to specific addr */
	if (!extra && to_trace && cfg->trace_addr) {
		smfi_addrcpt (ctx, cfg->trace_addr);
		smfi_setpriv (ctx, priv);
	}


	return (r > 0 ? res_action : r);
}
Exemple #7
0
static sfsistat
cb_eom(SMFICTX *ctx)
{
	struct context *context;
	sfsistat action = SMFIS_CONTINUE;
	char buf[2048];
	int pos = 0, retry = 0;

	if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
		msg(LOG_ERR, NULL, "cb_eom: smfi_getpriv");
		return (SMFIS_ACCEPT);
	}
	msg(LOG_DEBUG, context, "cb_eom()");
	if (context->fd < 0)
		goto done;
	context->symbols[0] = 0;
	/* no more writing data to spamd, want to read result now */
	if (shutdown(context->fd, SHUT_WR)) {
		msg(LOG_ERR, context, "cb_eom: shutdown: %s", strerror(errno));
		goto done;
	}
	if (fcntl(context->fd, F_SETFL, fcntl(context->fd, F_GETFL) |
	    O_NONBLOCK)) {
		msg(LOG_ERR, context, "cb_eom: fcntl: %s", strerror(errno));
		goto done;
	}
	/* try at most 6 times (10 seconds timeout each) */
	while (action == SMFIS_CONTINUE && retry < 6) {
		fd_set fds;
		struct timeval tv;
		int r, i;
		char b[8192];

		FD_ZERO(&fds);
		FD_SET(context->fd, &fds);
		tv.tv_sec = 10;
		tv.tv_usec = 0;
		r = select(context->fd + 1, &fds, NULL, NULL, &tv);
		if (r < 0) {
			if (errno != EINTR) {
				msg(LOG_ERR, context, "cb_eom: select: %s",
				    strerror(errno));
				break;
			}
			continue;
		} else if (r == 0 || !FD_ISSET(context->fd, &fds)) {
			retry++;
			msg(LOG_DEBUG, context, "cb_eom: waiting for "
			    "spamd reply (retry %d)", retry);
			continue;
		}
		r = read(context->fd, b, sizeof(b));
		if (r < 0) {
			if (errno != EINTR) {
				msg(LOG_ERR, context, "cb_eom: read: %s",
				    strerror(errno));
				break;
			}
			continue;
		} else if (r == 0)
			/* connection closed by spamd */
			break;
		for (i = 0; i < r; ++i)
			if (b[i] == '\n' || pos == sizeof(buf) - 1) {
				if (pos > 0 && buf[pos - 1] == '\r')
					buf[pos - 1] = 0;
				else
					buf[pos] = 0;
				/* sets action when done */
				spamd_reply(buf, context, &action);
				pos = 0;
			} else
				buf[pos++] = b[i];
	}
	if (retry == 6)
		msg(LOG_ERR, context, "cb_eom: spamd connection timed out");
done:
	if (context->fd >= 0) {
		close(context->fd);
		context->fd = -1;
	}
	/* either way, we don't want to continue */
	if (action == SMFIS_CONTINUE)
		action = (context->spam && context->score > REJECT_SPAM_LEVEL) ? SMFIS_REJECT : SMFIS_ACCEPT;
	msg(action == SMFIS_REJECT ? LOG_NOTICE : LOG_INFO, context,
	    "%s (%s %.1f/%.1f%s%s), From: %s, To: %s, Subject: %s",
	    (action == SMFIS_REJECT ? "REJECT" : "ACCEPT"),
	    (context->spam ? "SPAM" : "ham"), context->score, context->threshold,
	    (context->symbols[0] ? " " :  ""), context->symbols,
	    context->hdr_from, context->hdr_to, context->hdr_subject);
	if (action == SMFIS_REJECT) {
		char m[1024];

		snprintf(m, sizeof(m), "Spam (score %.1f)", context->score);
               if (smfi_setreply(ctx, RCODE_REJECT, XCODE_REJECT, m) !=
		    MI_SUCCESS)
			msg(LOG_ERR, context, "smfi_setreply");
	} else {
	 char m[1024];
		int j=0;
		int starcnt = (int)context->score;
		const char *star = "*";
		char stars[1024];
		stars[0]='\0';
		snprintf(m, sizeof(m), "%s, %s%.1f %s%.1f%s%s",
		    (context->spam ? "Yes" : "No"),"score=",
		    context->score, "required=",context->threshold,
		    (context->symbols[0] ? " " :  ""), context->symbols);

                if (smfi_addheader(ctx, "X-Spam-Flag", context->spam ? "YES" : "NO") != MI_SUCCESS) {
		      msg(LOG_ERR, context, "smfi_addheader");
        }

        if (smfi_addheader(ctx, "X-Spam-Status", m) != MI_SUCCESS) {
            msg(LOG_ERR, context, "smfi_addheader");
        }

        for (j = 0; j < starcnt; j++) {
            strlcat(stars, star, sizeof (stars));
        }
        if (smfi_addheader(ctx, "X-Spam-Level", stars) != MI_SUCCESS) {
            msg(LOG_ERR, context, "smfi_addheader");
        }


    }
    context->pos = context->hdr_from[0] = context->hdr_to[0] =
            context->hdr_subject[0] = context->state = context->spam =
            context->symbols[0] = 0;
    context->score = context->threshold = 0.0;
    return (action);
}