int _ds_shutdown_storage (DSPAM_CTX * CTX) { struct _hash_drv_storage *s; struct nt_node *node_nt; struct nt_c c_nt; int lock_result; if (!CTX || !CTX->storage) return EINVAL; s = (struct _hash_drv_storage *) CTX->storage; /* Close open file handles to directories (iteration functions) */ node_nt = c_nt_first (s->dir_handles, &c_nt); while (node_nt != NULL) { DIR *dir; dir = (DIR *) node_nt->ptr; closedir (dir); node_nt = c_nt_next (s->dir_handles, &c_nt); } nt_destroy (s->dir_handles); if (CTX->operating_mode != DSM_CLASSIFY) _hash_drv_set_spamtotals (CTX); /* Close connection to hash database only if we're not concurrent */ if (!s->dbh_attached) { _hash_drv_close(s->map); free(s->map); lock_result = _hash_drv_lock_free (s, (CTX->group) ? CTX->group : CTX->username); if (lock_result < 0) return EUNKNOWN; } free (CTX->storage); CTX->storage = NULL; return 0; }
int _ds_shutdown_storage (DSPAM_CTX * CTX) { struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage; struct nt_node *node_nt; struct nt_c c_nt; if (s->dbh == NULL) { LOGDEBUG ("_ds_shutdown_storage: invalid database handle (NULL)"); return EINVAL; } node_nt = c_nt_first (s->dir_handles, &c_nt); while (node_nt != NULL) { DIR *dir; dir = (DIR *) node_nt->ptr; closedir (dir); node_nt = c_nt_next (s->dir_handles, &c_nt); } nt_destroy (s->dir_handles); /* Store spam totals on shutdown */ if (CTX->username != NULL && CTX->operating_mode != DSM_CLASSIFY) { _sqlite_drv_set_spamtotals (CTX); } if (!s->dbh_attached) sqlite_close(s->dbh); s->dbh = NULL; free(s); CTX->storage = NULL; return 0; }
int _ds_tokenize_ngram( DSPAM_CTX *CTX, char *headers, char *body, ds_diction_t diction) { char *token; /* current token */ char *previous_token = NULL; /* used for bigrams (chained tokens) */ char *line = NULL; /* header broken up into lines */ char *ptrptr; char heading[128]; /* current heading */ int l, tokenizer = CTX->tokenizer; struct nt *header = NULL; struct nt_node *node_nt; struct nt_c c_nt; /* Tokenize URLs in message */ if (_ds_match_attribute(CTX->config->attributes, "ProcessorURLContext", "on")) { _ds_url_tokenize(diction, body, "http://"); _ds_url_tokenize(diction, body, "www."); _ds_url_tokenize(diction, body, "href="); } /* * Header Tokenization */ header = nt_create (NT_CHAR); if (header == NULL) { LOG (LOG_CRIT, ERR_MEM_ALLOC); return EUNKNOWN; } line = strtok_r (headers, "\n", &ptrptr); while (line) { nt_add (header, line); line = strtok_r (NULL, "\n", &ptrptr); } node_nt = c_nt_first (header, &c_nt); heading[0] = 0; while (node_nt) { int multiline; #ifdef VERBOSE LOGDEBUG("processing line: %s", node_nt->ptr); #endif line = node_nt->ptr; token = strtok_r (line, ":", &ptrptr); if (token && token[0] != 32 && token[0] != 9 && !strstr (token, " ")) { multiline = 0; strlcpy (heading, token, 128); previous_token = NULL; } else { multiline = 1; } #ifdef VERBOSE LOGDEBUG ("Reading '%s' header from: '%s'", heading, line); #endif if (CTX->flags & DSF_WHITELIST) { /* Use the entire From: line for auto-whitelisting */ if (!strcmp(heading, "From")) { char wl[256]; char *fromline = line + 5; unsigned long long whitelist_token; if (fromline[0] == 32) fromline++; snprintf(wl, sizeof(wl), "%s*%s", heading, fromline); whitelist_token = _ds_getcrc64(wl); ds_diction_touch(diction, whitelist_token, wl, 0); diction->whitelist_token = whitelist_token; } } /* Received headers use a different set of delimiters to preserve things like ip addresses */ token = strtok_r ((multiline) ? line : NULL, DELIMITERS_HEADING, &ptrptr); while (token) { l = strlen(token); if (l >= 1 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing '%s' token in '%s' header", token, heading); #endif /* Process "current" token */ if (!_ds_process_header_token (CTX, token, previous_token, diction, heading) && (tokenizer == DSZ_CHAIN)) { previous_token = token; } } token = strtok_r (NULL, DELIMITERS_HEADING, &ptrptr); } previous_token = NULL; node_nt = c_nt_next (header, &c_nt); } nt_destroy (header); /* * Body Tokenization */ #ifdef VERBOSE LOGDEBUG("parsing message body"); #endif token = strtok_r (body, DELIMITERS, &ptrptr); while (token != NULL) { l = strlen (token); if (l >= 1 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing body token '%s'", token); #endif /* Process "current" token */ if ( !_ds_process_body_token(CTX, token, previous_token, diction) && tokenizer == DSZ_CHAIN) { previous_token = token; } } token = strtok_r (NULL, DELIMITERS, &ptrptr); } #ifdef VERBOSE LOGDEBUG("Finished tokenizing (ngram) message"); #endif /* Final token reassembly (anything left in the buffer) */ return 0; }
int _ds_degenerate_message(DSPAM_CTX *CTX, buffer * header, buffer * body) { char *decode = NULL; struct nt_node *node_nt, *node_header; struct nt_c c_nt, c_nt2; int i = 0; char heading[1024]; if (! CTX->message) { LOG (LOG_WARNING, "_ds_degenerate_message() failed: CTX->message is NULL"); return EUNKNOWN; } /* Iterate through each component and create large header/body buffers */ node_nt = c_nt_first (CTX->message->components, &c_nt); while (node_nt != NULL) { struct _ds_message_part *block = (struct _ds_message_part *) node_nt->ptr; #ifdef VERBOSE LOGDEBUG ("Processing component %d", i); #endif if (! block->headers || ! block->headers->items) { #ifdef VERBOSE LOGDEBUG (" : End of Message Identifier"); #endif } else { struct _ds_header_field *current_header; /* Accumulate the headers */ node_header = c_nt_first (block->headers, &c_nt2); while (node_header != NULL) { current_header = (struct _ds_header_field *) node_header->ptr; snprintf (heading, sizeof (heading), "%s: %s\n", current_header->heading, current_header->data); buffer_cat (header, heading); node_header = c_nt_next (block->headers, &c_nt2); } decode = block->body->data; if (block->media_type == MT_TEXT || block->media_type == MT_MESSAGE || block->media_type == MT_UNKNOWN || (block->media_type == MT_MULTIPART && !i)) { /* Accumulate the bodies, skip attachments */ if ( ( block->encoding == EN_BASE64 || block->encoding == EN_QUOTED_PRINTABLE) && ! block->original_signed_body) { if (block->content_disposition != PCD_ATTACHMENT) { LOGDEBUG ("decoding message block from encoding type %d", block->encoding); decode = _ds_decode_block (block); } } /* We found a tokenizable body component, add prefilters */ if (decode) { char *decode2 = NULL; char *decode3 = NULL; /* -- PREFILTERS BEGIN -- */ /* Hexadecimal 8-Bit Encodings */ if (block->encoding == EN_8BIT) { decode2 = _ds_decode_hex8bit(decode); } else { decode2 = strdup(decode); } /* HTML-Specific Filters */ if (decode2) { if (block->media_subtype == MST_HTML) { decode3 = _ds_strip_html(decode2); } else { decode3 = strdup(decode2); } free(decode2); } /* -- PREFILTERS END -- */ if (decode3) { buffer_cat (body, decode3); free(decode3); } /* If we've decoded the body, save the original copy */ if (decode != block->body->data) { block->original_signed_body = block->body; block->body = buffer_create (decode); free (decode); } } } } #ifdef VERBOSE LOGDEBUG ("Getting next message component"); #endif node_nt = c_nt_next (CTX->message->components, &c_nt); i++; } /* while (node_nt != NULL) */ if (header->data == NULL) buffer_cat (header, " "); if (body->data == NULL) buffer_cat (body, " "); return 0; }
int _ds_tokenize_sparse( DSPAM_CTX *CTX, char *headers, char *body, ds_diction_t diction) { int i; char *token; /* current token */ char *previous_tokens[SPARSE_WINDOW_SIZE]; /* sparse chain */ char *line = NULL; /* header broken up into lines */ char *ptrptr; char *bitpattern; char heading[128]; /* current heading */ int l; struct nt *header = NULL; struct nt_node *node_nt; struct nt_c c_nt; for(i=0;i<SPARSE_WINDOW_SIZE;i++) previous_tokens[i] = NULL; bitpattern = _ds_generate_bitpattern(_ds_pow2(SPARSE_WINDOW_SIZE)); /* Tokenize URLs in message */ if (_ds_match_attribute(CTX->config->attributes, "ProcessorURLContext", "on")) { _ds_url_tokenize(diction, body, "http://"); _ds_url_tokenize(diction, body, "www."); _ds_url_tokenize(diction, body, "href="); } /* * Header Tokenization */ header = nt_create (NT_CHAR); if (header == NULL) { LOG (LOG_CRIT, ERR_MEM_ALLOC); free(bitpattern); return EUNKNOWN; } line = strtok_r (headers, "\n", &ptrptr); while (line) { nt_add (header, line); line = strtok_r (NULL, "\n", &ptrptr); } node_nt = c_nt_first (header, &c_nt); heading[0] = 0; while (node_nt) { int multiline; #ifdef VERBOSE LOGDEBUG("processing line: %s", node_nt->ptr); #endif _ds_sparse_clear(previous_tokens); line = node_nt->ptr; token = strtok_r (line, ":", &ptrptr); if (token && token[0] != 32 && token[0] != 9 && !strstr (token, " ")) { multiline = 0; strlcpy (heading, token, 128); _ds_sparse_clear(previous_tokens); } else { multiline = 1; } #ifdef VERBOSE LOGDEBUG ("Reading '%s' header from: '%s'", heading, line); #endif if (CTX->flags & DSF_WHITELIST) { /* Use the entire From: line for auto-whitelisting */ if (!strcmp(heading, "From")) { char wl[256]; char *fromline = line + 5; unsigned long long whitelist_token; if (fromline[0] == 32) fromline++; snprintf(wl, sizeof(wl), "%s*%s", heading, fromline); whitelist_token = _ds_getcrc64(wl); ds_diction_touch(diction, whitelist_token, wl, 0); diction->whitelist_token = whitelist_token; } } /* Received headers use a different set of delimiters to preserve things like ip addresses */ token = strtok_r ((multiline) ? line : NULL, SPARSE_DELIMITERS_HEADING, &ptrptr); while (token) { l = strlen(token); if (l > 0 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing '%s' token in '%s' header", token, heading); #endif _ds_map_header_token (CTX, token, previous_tokens, diction, heading, bitpattern); } token = strtok_r (NULL, SPARSE_DELIMITERS_HEADING, &ptrptr); } for(i=0;i<SPARSE_WINDOW_SIZE;i++) { _ds_map_header_token(CTX, NULL, previous_tokens, diction, heading, bitpattern); } _ds_sparse_clear(previous_tokens); node_nt = c_nt_next (header, &c_nt); } nt_destroy (header); /* * Body Tokenization */ #ifdef VERBOSE LOGDEBUG("parsing message body"); #endif token = strtok_r (body, SPARSE_DELIMITERS, &ptrptr); while (token != NULL) { l = strlen (token); if (l > 0 && l < 50) { #ifdef VERBOSE LOGDEBUG ("Processing body token '%s'", token); #endif /* Process "current" token */ _ds_map_body_token (CTX, token, previous_tokens, diction, bitpattern); } token = strtok_r (NULL, SPARSE_DELIMITERS, &ptrptr); } for(i=0;i<SPARSE_WINDOW_SIZE;i++) { _ds_map_body_token(CTX, NULL, previous_tokens, diction, bitpattern); } _ds_sparse_clear(previous_tokens); free(bitpattern); #ifdef VERBOSE LOGDEBUG("Finished tokenizing (sparse) message"); #endif return 0; }
char * _ds_get_nextuser (DSPAM_CTX * CTX) { static char user[MAX_FILENAME_LENGTH]; static char path[MAX_FILENAME_LENGTH]; struct _sqlite_drv_storage *s = (struct _sqlite_drv_storage *) CTX->storage; struct nt_node *node_nt, *prev; struct nt_c c_nt; char *x = NULL, *y; DIR *dir = NULL; struct dirent *entry; if (s->dir_handles->items == 0) { char filename[MAX_FILENAME_LENGTH]; snprintf(filename, MAX_FILENAME_LENGTH, "%s/data", CTX->home); dir = opendir (filename); if (dir == NULL) { LOG (LOG_WARNING, "unable to open directory '%s' for reading: %s", CTX->home, strerror (errno)); return NULL; } nt_add (s->dir_handles, (void *) dir); strlcpy (path, filename, sizeof (path)); } else { node_nt = c_nt_first (s->dir_handles, &c_nt); while (node_nt != NULL) { if (node_nt->next == NULL) dir = (DIR *) node_nt->ptr; node_nt = c_nt_next (s->dir_handles, &c_nt); } } while ((entry = readdir (dir)) != NULL) { struct stat st; char filename[MAX_FILENAME_LENGTH]; snprintf (filename, sizeof (filename), "%s/%s", path, entry->d_name); if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, "..")) continue; if (stat (filename, &st)) { continue; } /* push a new directory */ if (st.st_mode & S_IFDIR) { DIR *ndir; ndir = opendir (filename); if (ndir == NULL) continue; strlcat (path, "/", sizeof (path)); strlcat (path, entry->d_name, sizeof (path)); nt_add (s->dir_handles, (void *) ndir); return _ds_get_nextuser (CTX); } else if (!strncmp (entry->d_name + strlen (entry->d_name) - 4, ".sdb", 4)) { strlcpy (user, entry->d_name, sizeof (user)); user[strlen (user) - 4] = 0; return user; } } /* pop current directory */ y = strchr (path, '/'); while (y != NULL) { x = y; y = strchr (x + 1, '/'); } if (x) x[0] = 0; /* pop directory handle from list */ node_nt = c_nt_first (s->dir_handles, &c_nt); prev = NULL; while (node_nt != NULL) { if (node_nt->next == NULL) { dir = (DIR *) node_nt->ptr; closedir (dir); if (prev != NULL) { prev->next = NULL; s->dir_handles->insert = NULL; } else s->dir_handles->first = NULL; free (node_nt); s->dir_handles->items--; break; } prev = node_nt; node_nt = c_nt_next (s->dir_handles, &c_nt); } if (s->dir_handles->items > 0) return _ds_get_nextuser (CTX); /* done */ user[0] = 0; return NULL; }
void *process_connection(void *ptr) { char *server_ident = _ds_read_attribute(agent_config, "ServerIdent"); THREAD_CTX *TTX = (THREAD_CTX *) ptr; AGENT_CTX *ATX = NULL; char *input, *cmdline = NULL, *token, *ptrptr; buffer *message = NULL; char *parms=NULL, *p=NULL; int i, locked = -1, invalid = 0; int server_mode = SSM_DSPAM; char *argv[64]; char buf[1024]; int tries = 0; int argc = 0; FILE *fd = 0; if (_ds_read_attribute(agent_config, "ServerMode") && !strcasecmp(_ds_read_attribute(agent_config, "ServerMode"), "standard")) { server_mode = SSM_STANDARD; } if (_ds_read_attribute(agent_config, "ServerMode") && !strcasecmp(_ds_read_attribute(agent_config, "ServerMode"), "auto")) { server_mode = SSM_AUTO; } /* Initialize a file descriptor hook for dspam to use as stdout */ fd = fdopen(TTX->sockfd, "w"); if (!fd) { close(TTX->sockfd); goto CLOSE; } setbuf(fd, NULL); TTX->packet_buffer = buffer_create(NULL); if (TTX->packet_buffer == NULL) goto CLOSE; /* * Send greeting banner * in auto mode, we want to look like a regular LMTP server so we don't * cause any compatibility problems. in dspam mode, we can change this. */ snprintf(buf, sizeof(buf), "%d DSPAM %sLMTP %s %s", LMTP_GREETING, (server_mode == SSM_DSPAM) ? "D" : "", VERSION, (server_mode == SSM_DSPAM) ? "Authentication Required" : "Ready"); if (send_socket(TTX, buf)<=0) goto CLOSE; TTX->authenticated = 0; /* LHLO */ input = daemon_expect(TTX, "LHLO"); if (input == NULL) goto CLOSE; if (server_mode == SSM_AUTO && input[4]) { char buff[128]; /* * Auto-detect the server mode based on whether or not the ident is * assigned a password in dspam.conf */ snprintf(buff, sizeof(buff), "ServerPass.%s", input + 5); chomp(buff); if (_ds_read_attribute(agent_config, buff)) server_mode = SSM_DSPAM; else server_mode = SSM_STANDARD; } free(input); /* Advertise extensions */ if (daemon_extension(TTX, (server_ident) ? server_ident : "localhost.localdomain")<=0) goto CLOSE; if (daemon_extension(TTX, "PIPELINING")<=0) goto CLOSE; if (daemon_extension(TTX, "ENHANCEDSTATUSCODES")<=0) goto CLOSE; if (server_mode == SSM_DSPAM) if (daemon_extension(TTX, "DSPAMPROCESSMODE")<=0) goto CLOSE; if (daemon_extension(TTX, "8BITMIME")<=0) goto CLOSE; if (daemon_reply(TTX, LMTP_OK, "", "SIZE")<=0) goto CLOSE; /* Main protocol loop */ while(1) { char processmode[256]; parms = NULL; /* Configure a new agent context for each pass */ ATX = calloc(1, sizeof(AGENT_CTX)); if (ATX == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); daemon_reply(TTX, LMTP_TEMP_FAIL, "4.3.0", ERR_MEM_ALLOC); goto CLOSE; } if (initialize_atx(ATX)) { LOG(LOG_ERR, ERR_AGENT_INIT_ATX); daemon_reply(TTX, LMTP_BAD_CMD, "5.3.0", ERR_AGENT_INIT_ATX); goto CLOSE; } /* MAIL FROM (and authentication, if SSM_DSPAM) */ processmode[0] = 0; while(!TTX->authenticated) { input = daemon_expect(TTX, "MAIL FROM"); if (RSET(input)) goto RSET; if (input == NULL) goto CLOSE; else { char *pass, *ident; chomp(input); if (server_mode == SSM_STANDARD) { TTX->authenticated = 1; ATX->mailfrom[0] = 0; _ds_extract_address(ATX->mailfrom, input, sizeof(ATX->mailfrom)); if (daemon_reply(TTX, LMTP_OK, "2.1.0", "OK")<=0) { free(input); goto CLOSE; } } else { char id[256]; pass = ident = NULL; id[0] = 0; if (!_ds_extract_address(id, input, sizeof(id))) { pass = strtok_r(id, "@", &ptrptr); ident = strtok_r(NULL, "@", &ptrptr); } if (pass && ident) { char *serverpass; char *ptr, *ptr2, *ptr3; snprintf(buf, sizeof(buf), "ServerPass.%s", ident); serverpass = _ds_read_attribute(agent_config, buf); snprintf(buf, sizeof(buf), "ServerPass.%s", ident); if (serverpass && !strcmp(pass, serverpass)) { TTX->authenticated = 1; /* Parse PROCESSMODE service tag */ ptr = strstr(input, "DSPAMPROCESSMODE=\""); if (ptr) { char *mode; int i; ptr2 = strchr(ptr, '"')+1; mode = ptr2; while((ptr3 = strstr(ptr2, "\\\""))) ptr2 = ptr3+2; ptr3 = strchr(ptr2+2, '"'); if (ptr3) ptr3[0] = 0; strlcpy(processmode, mode, sizeof(processmode)); ptr = processmode; for(i=0; ptr[i]; i++) { if (ptr[i] == '\\' && ptr[i+1] == '"') { strcpy(ptr+i, ptr+i+1); } } LOGDEBUG("process mode: '%s'", processmode); } if (daemon_reply(TTX, LMTP_OK, "2.1.0", "OK")<=0) { free(input); goto CLOSE; } } } } free(input); if (!TTX->authenticated) { LOGDEBUG("fd %d authentication failure.", TTX->sockfd); if (daemon_reply(TTX, LMTP_AUTH_ERROR, "5.1.0", "Authentication Required")<=0) { free(input); goto CLOSE; } tries++; if (tries>=3) { struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; select(0, NULL, NULL, NULL, &tv); goto CLOSE; } } } } /* MAIL FROM response */ snprintf(buf, sizeof(buf), "%d OK", LMTP_OK); argc = 1; argv[0] = "dspam"; argv[1] = 0; /* Load open-LMTP configuration parameters */ if (server_mode == SSM_STANDARD) { parms = _ds_read_attribute(agent_config, "ServerParameters"); if (parms) { p = strdup(parms); if (p) { token = strtok_r(p, " ", &ptrptr); while(token != NULL && argc<63) { argv[argc] = token; argc++; argv[argc] = 0; token = strtok_r(NULL, " ", &ptrptr); } } } } /* RCPT TO */ while(ATX->users->items == 0 || invalid) { free(cmdline); cmdline = daemon_getline(TTX, 300); while(cmdline && (!strncasecmp(cmdline, "RCPT TO:", 8) || !strncasecmp(cmdline, "RSET", 4))) { char username[256]; char *at = NULL; if (!strncasecmp(cmdline, "RSET", 4)) { snprintf(buf, sizeof(buf), "%d OK", LMTP_OK); if (send_socket(TTX, buf)<=0) goto CLOSE; goto RSET; } if (_ds_extract_address(username, cmdline, sizeof(username)) || username[0] == 0 || username[0] == '-' || username[0] == '@') { if ((server_mode == SSM_DSPAM) || (server_mode == SSM_STANDARD && _ds_validate_address(username) == 0)) { daemon_reply(TTX, LMTP_BAD_CMD, "5.1.2", ERR_LMTP_BAD_RCPT); goto GETCMD; } } if (_ds_match_attribute(agent_config, "Broken", "case")) lc(username, username); /* Chop of @.* from the recipient */ if (_ds_match_attribute(agent_config, "StripRcptDomain", "on")) { at = strchr(username, '@'); if (at != NULL) *at = '\0'; } if (server_mode == SSM_DSPAM) { nt_add(ATX->users, username); } else { if (!parms || !strstr(parms, "--user ")) nt_add(ATX->users, username); if (!ATX->recipients) { ATX->recipients = nt_create(NT_CHAR); if (ATX->recipients == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); goto CLOSE; } } if (at != NULL) *at = '@'; /* always add complete address (user@domain) to recipient list */ nt_add(ATX->recipients, username); } if (daemon_reply(TTX, LMTP_OK, "2.1.5", "OK")<=0) goto CLOSE; GETCMD: free(cmdline); cmdline = daemon_getline(TTX, 300); } if (cmdline == NULL) goto CLOSE; if (!strncasecmp(cmdline, "RSET", 4)) { snprintf(buf, sizeof(buf), "%d OK", LMTP_OK); if (send_socket(TTX, buf)<=0) goto CLOSE; goto RSET; } if (!strncasecmp(cmdline, "quit", 4)) { daemon_reply(TTX, LMTP_OK, "2.0.0", "OK"); goto CLOSE; } /* Parse DSPAMPROCESSMODE input and set up process arguments */ if (server_mode == SSM_DSPAM && processmode[0] != 0) { token = strtok_r(processmode, " ", &ptrptr); while(token != NULL && argc<63) { argv[argc] = token; argc++; argv[argc] = 0; token = strtok_r(NULL, " ", &ptrptr); } } invalid = 0; if (process_arguments(ATX, argc, argv) || apply_defaults(ATX)) { LOG(LOG_ERR, ERR_AGENT_INIT_ATX); daemon_reply(TTX, LMTP_NO_RCPT, "5.1.0", ERR_AGENT_INIT_ATX); invalid = 1; } else if (ATX->users->items == 0) { daemon_reply(TTX, LMTP_NO_RCPT, "5.1.1", ERR_AGENT_USER_UNDEFINED); } } ATX->sockfd = fd; ATX->sockfd_output = 0; /* Something's terribly misconfigured */ if (check_configuration(ATX)) { LOG(LOG_ERR, ERR_AGENT_MISCONFIGURED); daemon_reply(TTX, LMTP_BAD_CMD, "5.3.5", ERR_AGENT_MISCONFIGURED); goto CLOSE; } /* DATA */ if (cmdline != NULL) { if (strncasecmp(cmdline, "DATA", 4)) { if (daemon_reply(TTX, LMTP_BAD_CMD, "5.0.0", "Need DATA Here")<0) goto CLOSE; input = daemon_expect(TTX, "DATA"); if (input == NULL) goto CLOSE; if (RSET(input)) goto RSET; } } if (daemon_reply(TTX, LMTP_DATA, "", INFO_LMTP_DATA)<=0) goto CLOSE; /* * Read in the message from a DATA. I personally like to just hang up on * a client stupid enough to pass in a NULL message for DATA, but you're * welcome to do whatever you want. */ message = read_sock(TTX, ATX); if (message == NULL || message->data == NULL || message->used == 0) { daemon_reply(TTX, LMTP_FAILURE, "5.2.0", ERR_LMTP_MSG_NULL); goto CLOSE; } /* * Lock a database handle. We currently use the modulus of the socket * id against the number of database connections in the cache. This * seems to work rather well, as we would need to lock up the entire * cache to wrap back around. And if we do wrap back around, that means * we're busy enough to justify spinning on the current lock (vs. seeking * out a free handle, which there likely are none). */ i = (TTX->sockfd % TTX->DTX->connection_cache); LOGDEBUG("using database handle id %d", i); if (TTX->DTX->flags & DRF_RWLOCK) { if (ATX->operating_mode == DSM_CLASSIFY || ATX->training_mode == DST_NOTRAIN || (ATX->training_mode == DST_TOE && ATX->classification == DSR_NONE)) { pthread_rwlock_rdlock(&TTX->DTX->connections[i]->rwlock); } else { pthread_rwlock_wrlock(&TTX->DTX->connections[i]->rwlock); } } else { pthread_mutex_lock(&TTX->DTX->connections[i]->lock); } LOGDEBUG("handle locked"); ATX->dbh = TTX->DTX->connections[i]->dbh; locked = i; /* Process the message by tying back into the agent functions */ ATX->results = nt_create(NT_PTR); if (ATX->results == NULL) { LOG(LOG_CRIT, ERR_MEM_ALLOC); goto CLOSE; } process_users(ATX, message); /* * Unlock the database handle as soon as we're done. We also need to * refresh our handle index with a new handle if for some reason we * had to re-establish a dewedged connection. */ if (TTX->DTX->connections[locked]->dbh != ATX->dbh) TTX->DTX->connections[locked]->dbh = ATX->dbh; if (TTX->DTX->flags & DRF_RWLOCK) { pthread_rwlock_unlock(&TTX->DTX->connections[locked]->rwlock); } else { pthread_mutex_unlock(&TTX->DTX->connections[locked]->lock); } locked = -1; /* Send a terminating '.' if --stdout in 'dspam' mode */ if (ATX->sockfd_output) { if (send_socket(TTX, ".")<=0) goto CLOSE; /* Otherwise, produce standard delivery results */ } else { struct nt_node *node_nt, *node_res = NULL; struct nt_c c_nt; if (ATX->recipients) node_nt = c_nt_first(ATX->recipients, &c_nt); else node_nt = c_nt_first(ATX->users, &c_nt); if (ATX->results) node_res = ATX->results->first; while(node_res && node_nt != NULL) { agent_result_t result = (agent_result_t) node_res->ptr; if (result != NULL && result->exitcode == ERC_SUCCESS) { if (server_mode == SSM_DSPAM) { snprintf(buf, sizeof(buf), "%d 2.6.0 <%s> Message accepted for delivery: %s", LMTP_OK, (char *) node_nt->ptr, (result->classification == DSR_ISSPAM) ? "SPAM" : "INNOCENT"); } else { snprintf(buf, sizeof(buf), "%d 2.6.0 <%s> Message accepted for delivery", LMTP_OK, (char *) node_nt->ptr); } } else { if (result != NULL && result->exitcode == ERC_PERMANENT_DELIVERY) { snprintf(buf, sizeof(buf), "%d 5.3.0 <%s> %s", LMTP_FAILURE, (char *) node_nt->ptr, (result->text[0]) ? result->text : "Permanent error occured"); } else { if (result != NULL && result->text[0]) { snprintf(buf, sizeof(buf), "%d 4.3.0 <%s> %s", LMTP_TEMP_FAIL, (char *) node_nt->ptr, result->text); } else { snprintf(buf, sizeof(buf), "%d 4.3.0 <%s> Error occured during %s", LMTP_TEMP_FAIL, (char *) node_nt->ptr, (result != NULL && result->exitcode == ERC_DELIVERY) ? "delivery" : "processing"); } } } if (send_socket(TTX, buf)<=0) goto CLOSE; if (ATX->recipients) node_nt = c_nt_next(ATX->recipients, &c_nt); else node_nt = c_nt_next(ATX->users, &c_nt); if (node_res) node_res = node_res->next; } } /* Cleanup and get ready for another message */ RSET: fflush(fd); buffer_destroy(message); message = NULL; if (ATX != NULL) { nt_destroy(ATX->users); nt_destroy(ATX->recipients); nt_destroy(ATX->results); free(ATX); ATX = NULL; free(cmdline); cmdline = NULL; TTX->authenticated = 0; /* argc = 0; */ } free(p); p = NULL; } /* while(1) */ /* Close connection and return */ CLOSE: if (locked>=0) pthread_mutex_unlock(&TTX->DTX->connections[locked]->lock); if (fd) fclose(fd); buffer_destroy(TTX->packet_buffer); if (message) buffer_destroy(message); if (ATX != NULL) { nt_destroy(ATX->users); nt_destroy(ATX->recipients); nt_destroy(ATX->results); } free(ATX); free(cmdline); free(TTX); decrement_thread_count(); pthread_exit(0); return 0; }