X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fspam.c;h=efeb6931235db39787a0e4a7a6341583dab7a2b6;hb=80832b14de1d9c1f1568ab0bfa3dee225fbaa5d2;hp=beec82363a16f7852b2d3a1db5614d0365ef63ab;hpb=89dec7b604c1d16d8762e99443ba7bf2a3086c21;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/spam.c b/src/src/spam.c index beec82363..efeb69312 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -1,5 +1,3 @@ -/* $Cambridge: exim/src/src/spam.c,v 1.17 2008/07/18 17:55:42 fanf2 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ @@ -20,15 +18,18 @@ uschar spam_report_buffer[32600]; uschar prev_user_name[128] = ""; int spam_ok = 0; int spam_rc = 0; +uschar *prev_spamd_address_work = NULL; -int spam(uschar **listptr) { +int +spam(uschar **listptr) +{ int sep = 0; uschar *list = *listptr; uschar *user_name; uschar user_name_buffer[128]; unsigned long mbox_size; FILE *mbox_file; - int spamd_sock; + int spamd_sock = -1; uschar spamd_buffer[32600]; int i, j, offset, result; uschar spamd_version[8]; @@ -54,60 +55,68 @@ int spam(uschar **listptr) { /* find the username from the option list */ if ((user_name = string_nextinlist(&list, &sep, user_name_buffer, - sizeof(user_name_buffer))) == NULL) { + sizeof(user_name_buffer))) == NULL) + { /* no username given, this means no scanning should be done */ return FAIL; - }; + } /* if username is "0" or "false", do not scan */ if ( (Ustrcmp(user_name,"0") == 0) || - (strcmpic(user_name,US"false") == 0) ) { + (strcmpic(user_name,US"false") == 0) ) return FAIL; - }; /* if there is an additional option, check if it is "true" */ - if (strcmpic(list,US"true") == 0) { + if (strcmpic(list,US"true") == 0) /* in that case, always return true later */ override = 1; - }; + + /* expand spamd_address if needed */ + if (*spamd_address == '$') + { + spamd_address_work = expand_string(spamd_address); + if (spamd_address_work == NULL) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "spamassassin acl condition: spamd_address starts with $, but expansion failed: %s", expand_string_message); + return DEFER; + } + } + else + spamd_address_work = spamd_address; + + /* check if previous spamd_address was expanded and has changed. dump cached results if so */ + if ( spam_ok + && prev_spamd_address_work != NULL + && Ustrcmp(prev_spamd_address_work, spamd_address_work) != 0 + ) + spam_ok = 0; /* if we scanned for this username last time, just return */ - if ( spam_ok && ( Ustrcmp(prev_user_name, user_name) == 0 ) ) { + if ( spam_ok && Ustrcmp(prev_user_name, user_name) == 0) if (override) return OK; else return spam_rc; - }; /* make sure the eml mbox file is spooled up */ - mbox_file = spool_mbox(&mbox_size); + mbox_file = spool_mbox(&mbox_size, NULL); - if (mbox_file == NULL) { + if (mbox_file == NULL) + { /* error while spooling */ log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: error while creating mbox spool file"); return DEFER; - }; + } start = time(NULL); - if (*spamd_address == '$') { - spamd_address_work = expand_string(spamd_address); - if (spamd_address_work == NULL) { - log_write(0, LOG_MAIN|LOG_PANIC, - "spamassassin acl condition: spamd_address starts with $, but expansion failed: %s", expand_string_message); - return DEFER; - } - } - else - spamd_address_work = spamd_address; - /* socket does not start with '/' -> network socket */ - if (*spamd_address_work != '/') { - time_t now = time(NULL); + if (*spamd_address_work != '/') + { int num_servers = 0; - int current_server = 0; - int start_server = 0; + int current_server; uschar *address = NULL; uschar *spamd_address_list_ptr = spamd_address_work; uschar address_buffer[256]; @@ -117,98 +126,119 @@ int spam(uschar **listptr) { and register their addresses */ while ((address = string_nextinlist(&spamd_address_list_ptr, &sep, address_buffer, - sizeof(address_buffer))) != NULL) { + sizeof(address_buffer))) != NULL) + { + /* Potential memory leak as we never free the store. */ spamd_address_container *this_spamd = (spamd_address_container *)store_get(sizeof(spamd_address_container)); /* grok spamd address and port */ - if( sscanf(CS address, "%s %u", this_spamd->tcp_addr, &(this_spamd->tcp_port)) != 2 ) { + if (sscanf(CS address, "%s %u", this_spamd->tcp_addr, &(this_spamd->tcp_port)) != 2) + { log_write(0, LOG_MAIN, "spam acl condition: warning - invalid spamd address: '%s'", address); continue; - }; + } spamd_address_vector[num_servers] = this_spamd; - num_servers++; - if (num_servers > 31) + if (++num_servers > 31) break; - }; + } /* check if we have at least one server */ - if (!num_servers) { + if (!num_servers) + { log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: no useable spamd server addresses in spamd_address configuration option."); (void)fclose(mbox_file); return DEFER; - }; + } - current_server = start_server = (int)now % num_servers; + while ( num_servers > 0 ) + { + int i; - while (1) { + /* Randomly pick a server to try */ + current_server = random_number( num_servers ); debug_printf("trying server %s, port %u\n", spamd_address_vector[current_server]->tcp_addr, spamd_address_vector[current_server]->tcp_port); /* contact a spamd */ - if ( (spamd_sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) { + if ( (spamd_sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) + { log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: error creating IP socket for spamd"); (void)fclose(mbox_file); return DEFER; - }; + } if (ip_connect( spamd_sock, AF_INET, spamd_address_vector[current_server]->tcp_addr, spamd_address_vector[current_server]->tcp_port, - 5 ) > -1) { + 5 ) > -1) /* connection OK */ break; - }; log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: warning - spamd connection to %s, port %u failed: %s", spamd_address_vector[current_server]->tcp_addr, spamd_address_vector[current_server]->tcp_port, strerror(errno)); - current_server++; - if (current_server >= num_servers) - current_server = 0; - if (current_server == start_server) { - log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: all spamd servers failed"); - (void)fclose(mbox_file); - (void)close(spamd_sock); - return DEFER; - }; - }; - } - else { + (void)close(spamd_sock); + + /* Remove the server from the list. XXX We should free the memory */ + num_servers--; + for ( i = current_server; i < num_servers; i++ ) + spamd_address_vector[i] = spamd_address_vector[i+1]; + } + + if ( num_servers == 0 ) + { + log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: all spamd servers failed"); + (void)fclose(mbox_file); + return DEFER; + } + } + else + { /* open the local socket */ - if ((spamd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + if ((spamd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: spamd: unable to acquire socket (%s)", strerror(errno)); (void)fclose(mbox_file); return DEFER; - } + } server.sun_family = AF_UNIX; Ustrcpy(server.sun_path, spamd_address_work); - if (connect(spamd_sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) { + if (connect(spamd_sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) + { log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: spamd: unable to connect to UNIX socket %s (%s)", spamd_address_work, strerror(errno) ); (void)fclose(mbox_file); (void)close(spamd_sock); return DEFER; + } } - } + if (spamd_sock == -1) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "programming fault, spamd_sock unexpectedly unset"); + (void)fclose(mbox_file); + (void)close(spamd_sock); + return DEFER; + } /* now we are connected to spamd on spamd_sock */ (void)string_format(spamd_buffer, @@ -218,14 +248,15 @@ int spam(uschar **listptr) { mbox_size); /* send our request */ - if (send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0) < 0) { + if (send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0) < 0) + { (void)close(spamd_sock); log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: spamd send failed: %s", strerror(errno)); (void)fclose(mbox_file); (void)close(spamd_sock); return DEFER; - }; + } /* now send the file */ /* spamd sometimes accepts conections but doesn't read data off @@ -242,9 +273,11 @@ int spam(uschar **listptr) { pollfd.events = POLLOUT; #endif (void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK); - do { + do + { read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file); - if (read > 0) { + if (read > 0) + { offset = 0; again: #ifndef NO_POLL_H @@ -262,44 +295,49 @@ again: if (result == -1 && errno == EINTR) goto again; - else if (result < 1) { + else if (result < 1) + { if (result == -1) log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: %s on spamd socket", strerror(errno)); - else { + else + { if (time(NULL) - start < SPAMD_TIMEOUT) - goto again; + goto again; log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: timed out writing spamd socket"); - } + } (void)close(spamd_sock); (void)fclose(mbox_file); return DEFER; - } + } wrote = send(spamd_sock,spamd_buffer + offset,read - offset,0); if (wrote == -1) - { - log_write(0, LOG_MAIN|LOG_PANIC, + { + log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: %s on spamd socket", strerror(errno)); (void)close(spamd_sock); (void)fclose(mbox_file); return DEFER; - } - if (offset + wrote != read) { + } + if (offset + wrote != read) + { offset += wrote; goto again; + } } } - } while (!feof(mbox_file) && !ferror(mbox_file)); - if (ferror(mbox_file)) { + + if (ferror(mbox_file)) + { log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: error reading spool file: %s", strerror(errno)); (void)close(spamd_sock); (void)fclose(mbox_file); return DEFER; - } + } (void)fclose(mbox_file); @@ -310,79 +348,83 @@ again: */ memset(spamd_buffer, 0, sizeof(spamd_buffer)); offset = 0; - while((i = ip_recv(spamd_sock, + while ((i = ip_recv(spamd_sock, spamd_buffer + offset, sizeof(spamd_buffer) - offset - 1, - SPAMD_TIMEOUT - time(NULL) + start)) > 0 ) { + SPAMD_TIMEOUT - time(NULL) + start)) > 0 ) offset += i; - } /* error handling */ - if((i <= 0) && (errno != 0)) { + if (i <= 0 && errno != 0) + { log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: error reading from spamd socket: %s", strerror(errno)); (void)close(spamd_sock); return DEFER; - } + } /* reading done */ (void)close(spamd_sock); /* dig in the spamd output and put the report in a multiline header, if requested */ - if( sscanf(CS spamd_buffer,"SPAMD/%7s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n", - spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) { + if (sscanf(CS spamd_buffer, + "SPAMD/%7s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n", + spamd_version, &spamd_score, &spamd_threshold, + &spamd_report_offset) != 3) + { /* try to fall back to pre-2.50 spamd output */ - if( sscanf(CS spamd_buffer,"SPAMD/%7s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n", - spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) { + if (sscanf(CS spamd_buffer, + "SPAMD/%7s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n", + spamd_version, &spamd_score, &spamd_threshold, + &spamd_report_offset) != 3 ) + { log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: cannot parse spamd output"); return DEFER; - }; - }; + } + } /* Create report. Since this is a multiline string, we must hack it into shape first */ p = &spamd_buffer[spamd_report_offset]; q = spam_report_buffer; - while (*p != '\0') { + while (*p != '\0') + { /* skip \r */ - if (*p == '\r') { + if (*p == '\r') + { p++; continue; - }; - *q = *p; - q++; - if (*p == '\n') { + } + *q++ = *p; + if (*p++ == '\n') + { /* add an extra space after the newline to ensure that it is treated as a header continuation line */ - *q = ' '; - q++; - }; - p++; - }; + *q++ = ' '; + } + } /* NULL-terminate */ - *q = '\0'; - q--; + *q-- = '\0'; /* cut off trailing leftovers */ - while (*q <= ' ') { - *q = '\0'; - q--; - }; + while (*q <= ' ') + *q-- = '\0'; + spam_report = spam_report_buffer; /* create spam bar */ spamd_score_char = spamd_score > 0 ? '+' : '-'; j = abs((int)(spamd_score)); i = 0; - if( j != 0 ) { - while((i < j) && (i <= MAX_SPAM_BAR_CHARS)) + if (j != 0) + while ((i < j) && (i <= MAX_SPAM_BAR_CHARS)) spam_bar_buffer[i++] = spamd_score_char; - } - else{ + else + { spam_bar_buffer[0] = '/'; i = 1; - } + } spam_bar_buffer[i] = '\0'; spam_bar = spam_bar_buffer; @@ -396,28 +438,29 @@ again: spam_score_int = spam_score_int_buffer; /* compare threshold against score */ - if (spamd_score >= spamd_threshold) { + if (spamd_score >= spamd_threshold) + { /* spam as determined by user's threshold */ spam_rc = OK; - } - else { + } + else + { /* not spam */ spam_rc = FAIL; - }; + } - /* remember user name and "been here" for it unless spamd_socket was expanded */ - if (spamd_address_work == spamd_address) { - Ustrcpy(prev_user_name, user_name); - spam_ok = 1; - } + /* remember expanded spamd_address if needed */ + if (spamd_address_work != spamd_address) + prev_spamd_address_work = string_copy(spamd_address_work); - if (override) { - /* always return OK, no matter what the score */ + /* remember user name and "been here" for it */ + Ustrcpy(prev_user_name, user_name); + spam_ok = 1; + + if (override) /* always return OK, no matter what the score */ return OK; - } - else { + else return spam_rc; - }; } #endif