/* * Sendmail wants to establish a connection to us */ static sfsistat blacklist_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) { const char *remoteIP; struct privdata *privdata; time_t now; int i; struct bip *bip; char ip[INET_ADDRSTRLEN]; /* IPv4 only */ if(hostname == NULL) { syslog(LOG_ERR, "blacklist_connect: hostname is null"); return SMFIS_CONTINUE; } if((hostaddr == NULL) || (&(((struct sockaddr_in *)(hostaddr))->sin_addr) == NULL)) /* * According to the sendmail API hostaddr is NULL if * "the type is not supported in the current version". What * the documentation doesn't say is the type of what. * * Possibly the input is not a TCP/IP socket e.g. stdin? */ return SMFIS_ACCEPT; remoteIP = inet_ntop(AF_INET, &((struct sockaddr_in *)(hostaddr))->sin_addr, ip, sizeof(ip)); #ifdef DEBUG printf("blacklist_milter: connection from %s\n", remoteIP); #endif syslog(LOG_INFO, "blacklist_milter: connection from %s", remoteIP); if(remoteIP == NULL) { #ifdef DEBUG puts("blacklist_connect: remoteIP is null"); #endif syslog(LOG_ERR, "blacklist_connect: remoteIP is null"); return SMFIS_CONTINUE; } if(strcmp(remoteIP, "127.0.0.1") == 0) { /*syslog(LOG_DEBUG, "blacklist_connect: not scanning outgoing messages");*/ #ifdef DEBUG puts("blacklist_connect: not scanning outgoing messages"); #endif return SMFIS_ACCEPT; } if(isLocalAddr(inet_addr(remoteIP))) { #ifdef DEBUG puts("blacklist_connect: not scanning local messages"); #endif /*syslog(LOG_DEBUG, "blacklist_connect: not scanning local messages");*/ return SMFIS_ACCEPT; } if(isWhiteList(remoteIP)) { /*syslog(LOG_DEBUG, "blacklist_connect: not scanning %s", remoteIP);*/ #ifdef DEBUG printf("blacklist_connect: not scanning %s\n", remoteIP); #endif return SMFIS_ACCEPT; } for(i = 0, bip = bips; i < NBLACKLISTED; i++, bip++) if(strcmp(bip->ip, remoteIP) == 0) { now = time((time_t *)0); if((now - bip->time) < TIMEOUT) { syslog(LOG_NOTICE, "Rejected connexion from blacklisted IP %s for %s\n", remoteIP, bip->reason); thread_sleep(2); /* waste their time */ smfi_setreply(ctx, "550", "5.7.1", "Your IP is blacklisted because you are sending spam"); bip->time = now; return SMFIS_REJECT; } else /* timeout */ bip->ip[0] = '\0'; break; } privdata = (struct privdata *)calloc(1, sizeof(struct privdata)); smfi_setpriv(ctx, privdata); strcpy(privdata->ip, remoteIP); return SMFIS_CONTINUE; }
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); }