static void dbt_close(dbt_t *dbt) { if (!dbt->dbt_open) { return; } if ((dbt->dbt_driver->dd_flags & DBT_LOCK)) { if(pthread_mutex_destroy(&dbt->dbt_mutex)) { log_sys_error("dbt_close: ptrhead_mutex_destroy"); } } DBT_DB_CLOSE(dbt); if (dbt->dbt_scheme) { var_delete(dbt->dbt_scheme); } dbt->dbt_open = 0; return; }
/* Takes a char pointer "key", variable "value", and "user_data" (not used), and frees the memory for "key" and "value" */ static gboolean var_free_func( gpointer key, gpointer value, gpointer user_data ) { free( key ); var_delete( (variable*)value, (fb_allocator*)user_data ); return TRUE; }
void cf_clear(void) { if (cf_config) { var_delete(cf_config); } return; }
var_t * vlist_record_from_table(var_t *scheme, var_t *table) { var_t *record = NULL, *vs, *vt; ll_t *ll; ll_entry_t *pos; void *data; var_type_t type; char *name; record = vlist_create(scheme->v_name, VF_KEEPNAME); if (record == NULL) { log_warning("vlist_record: vlist_create failed"); return NULL; } ll = scheme->v_data; pos = LL_START(ll); while ((vs = ll_next(ll, &pos))) { name = vs->v_name; vt = vtable_lookup(table, name); if (vt == NULL && vs->v_flags & VF_KEY) { log_error("vlist_record_from_table: \"%s\" is missing " "in vtable and declared as key", name); goto error; } data = vt == NULL ? NULL : vt->v_data; type = vt == NULL ? VT_NULL : vt->v_type; if (vlist_append_new(record, type, name, data, VF_COPY | vs->v_flags) == -1) { log_warning("vlist_record_from_table: vlist_append_new" " failed"); goto error; } } return record; error: if(record) { var_delete(record); } return NULL; }
static int hitlist_sql_safe_update(hitlist_t *hl, var_t *record, char *value_field, VAR_INT_T value_diff, char *expire_field, VAR_INT_T expire_diff) { var_t *record_copy = NULL; var_t *field; int status = -1; record_copy = VAR_COPY(record); if (record_copy == NULL) { log_error("hitlist_sql_safe_update: var_copy failed"); goto exit; } field = vlist_record_lookup(record_copy, value_field); if (field == NULL) { log_error("hitlist_sql_safe_update: vlist_get for %s failed", value_field); goto exit; } * (VAR_INT_T *) field->v_data = value_diff; field->v_flags |= VF_SQL_SAFE_UPDATE; field = vlist_record_lookup(record_copy, expire_field); if (field == NULL) { log_error("hitlist_sql_safe_update: vlist_get for %s failed", expire_field); goto exit; } * (VAR_INT_T *) field->v_data = expire_diff; field->v_flags |= VF_SQL_SAFE_UPDATE; status = dbt_db_set(&hl->hl_dbt, record_copy); if (status) { log_error("hitlist_sql_safe_update: dbt_db_set failed"); } exit: if (record_copy) { var_delete(record_copy); } return status; }
var_t * vlist_record(var_t *scheme, ...) { va_list ap; var_t *record = NULL, *v; int flags; void *data; ll_t *ll; ll_entry_t *pos; va_start(ap, scheme); record = vlist_create(scheme->v_name, VF_KEEPNAME); if (record == NULL) { log_warning("vlist_record: vlist_create failed"); return NULL; } ll = scheme->v_data; pos = LL_START(ll); while ((v = ll_next(ll, &pos))) { flags = v->v_flags; flags |= VF_KEEPDATA | VF_KEEPNAME; flags &= ~(VF_COPYNAME | VF_COPYDATA); data = va_arg(ap, void *); if (vlist_append_new(record, v->v_type, v->v_name, data, flags) == -1) { log_warning("vlist_record: vlist_append_new failed"); var_delete(record); return NULL; } } va_end(ap); return record; }
var_t * vlist_scheme(char *scheme, ...) { va_list ap; var_t *record = NULL; var_type_t type; int flags; char *name; record = vlist_create(scheme, VF_COPYNAME); if (record == NULL) { log_warning("vlist_scheme: vlist_create failed"); return NULL; } va_start(ap, scheme); for (;;) { name = va_arg(ap, char *); if (name == NULL) { break; } type = va_arg(ap, var_type_t); flags = va_arg(ap, int); if (vlist_append_new(record, type, name, NULL, flags) == -1) { log_warning("vlist_scheme: vlist_append_new failed"); var_delete(record); return NULL; } } va_end(ap); return record; }
static var_t * exp_eval_list(exp_t *exp, var_t *mailspec) { ll_t *exp_list = exp->ex_data; ll_entry_t *pos; exp_t *exp_item; var_t *var_item, *var_list = NULL; var_list = vlist_create(NULL, VF_EXP_FREE); if (var_list == NULL) { log_sys_error("exp_eval_list: malloc"); goto error; } pos = LL_START(exp_list); while ((exp_item = ll_next(exp_list, &pos))) { var_item = exp_eval(exp_item, mailspec); if (vlist_append(var_list, var_item)) { log_sys_error("exp_eval_list: malloc"); goto error; } } return var_list; error: if (var_list) { var_delete(var_list); } return NULL; }
void exp_free(var_t *v) { if (v == NULL) { return; } if ((v->v_flags & VF_EXP_FREE) == 0) { return; } if (v->v_type == VT_LIST && v->v_data != NULL) { ll_delete(v->v_data, (void *) exp_free); v->v_data = NULL; } var_delete(v); return; }
int vlist_append_new(var_t *list, var_type_t type, char *name, void *data, int flags) { var_t *v; v = var_create(type, name, data, flags); if (v == NULL) { log_warning("vlist_append_new: var_create failed"); return -1; } if (vlist_append(list, v) == 0) { return 0; } log_error("vlist_append_new: vlist_append failed"); var_delete(v); return -1; }
void exp_delete(exp_t *exp) { switch (exp->ex_type) { case EX_PARENTHESES: break; case EX_CONSTANT: var_delete(exp->ex_data); break; case EX_FUNCTION: exp_function_delete(exp); break; case EX_LIST: ll_delete(exp->ex_data, NULL); break; case EX_SYMBOL: case EX_VARIABLE: case EX_OPERATION: case EX_TERNARY_COND: case EX_MACRO: free(exp->ex_data); break; default: log_die(EX_SOFTWARE, "exp_delete: bad type"); } free(exp); return; }
static var_t * exp_eval_function_simple(char *name, acl_function_t *af, ll_t *args) { ll_t garbage; ll_entry_t *pos; void **argv = NULL; var_t *v = NULL; int argc; int size; int i; var_t *arg; /* * Initialize garbage */ ll_init(&garbage); /* * Check argc */ argc = args->ll_size; if (argc != af->af_argc) { log_error("exp_eval_function_simple: function \"%s\" requires " "%d arguments", name, af->af_argc); return NULL; } size = (argc + 1) * sizeof (void *); argv = (void **) malloc(size); if (argv == NULL) { log_sys_error("exp_eval_function_simple: malloc"); return NULL; } memset(argv, 0, size); /* * Prepare argv */ pos = LL_START(args); for (i = 0; (arg = ll_next(args, &pos)); ++i) { if (af->af_types[i] == arg->v_type) { argv[i] = arg->v_data; continue; } /* * Type casting required. Don't care about the remains of arg * (freed with args!). */ arg = var_cast_copy(af->af_types[i], arg); if (arg == NULL) { log_error("exp_eval_function_simple: var_cast_copy " "failed"); goto error; } /* * Need to free copy later */ if (LL_INSERT(&garbage, arg) == -1) { log_error("exp_eval_function_simlpe: LL_INSERT " "failed"); var_delete(arg); goto error; } argv[i] = arg->v_data; } v = af->af_callback.fc_simple(argc, argv); error: ll_clear(&garbage, (ll_delete_t) var_delete); if (argv) { free(argv); } return v; }
static var_t * exp_eval_regex(int op, var_t *left, var_t *right) { char *p; int e; char error[1024]; int flags = REG_EXTENDED | REG_NOSUB; int match; regex_t r; var_t *pattern_copy = NULL; var_t *str_copy = NULL; char *pattern; char *str; if (left == NULL || right == NULL) { return EXP_EMPTY; } if (left->v_data == NULL || right->v_data == NULL) { log_debug("exp_eval_regex: empty value"); return EXP_EMPTY; } // Make sure left is a string if (left->v_type != VT_STRING) { str_copy = var_cast_copy(VT_STRING, left); if (str_copy == NULL) { log_error("exp_eval_regex: var_cast_copy failed"); goto error; } str = str_copy->v_data; } else { str = left->v_data; } // Make sure right is a string if (right->v_type != VT_STRING) { pattern_copy = var_cast_copy(VT_STRING, right); if (pattern_copy == NULL) { log_error("exp_eval_regex: var_cast_copy failed"); goto error; } pattern = pattern_copy->v_data; } else { pattern = right->v_data; } // Test if pattern contains upper case chars flags = REG_EXTENDED | REG_NOSUB; for (p = pattern; *p; ++p) { if (isupper((int) *p)) { break; } } // If pattern is all lower perform case insensitiv matching if(*p == 0) { flags |= REG_ICASE; } e = regcomp(&r, pattern, flags); if (e) { regerror(e, &r, error, sizeof error); log_error("exp_eval_regex: regcomp: %s", error); goto error; } // Regexec returns 0 if pattern matched. match = regexec(&r, str, 0, NULL, 0); // free memory regfree(&r); if (str_copy) { var_delete(str_copy); } if (pattern_copy) { var_delete(pattern_copy); } if (op == NR) { match = !match; } if (match) { return EXP_FALSE; } return EXP_TRUE; error: if (str_copy) { var_delete(str_copy); } if (pattern_copy) { var_delete(pattern_copy); } return NULL; }
static int hitlist_lookup(milter_stage_t stage, char *name, var_t *attrs) { hitlist_t *hl; var_t *lookup = NULL; var_t *record = NULL; VAR_INT_T *value; VAR_INT_T *expire; var_t *addition; int success = -1; // Used for SQL_SAFE_UPDATE VAR_INT_T value_diff = 0; VAR_INT_T expire_diff = 0; int update_record = 0; hl = sht_lookup(hitlists, name); if (hl == NULL) { log_error("Unknown hitlist: %s", name); goto exit; } // If the DB is not open yet, there's a race on hl->hl_connected. if (pthread_mutex_lock(&hl->hl_mutex)) { log_sys_error("hitlist_db_open: pthread_mutex_lock"); goto exit; } // Open Database if (!hl->hl_connected) { if (hitlist_db_open(hl, attrs)) { log_error("hitlist_lookup: hitlist_db_open failed"); goto exit; } } // Create lookup record lookup = hitlist_record(hl, attrs, 1); if (lookup == NULL) { // Happens if a key is not set or NULL log_error("hitlist_lookup: hitlist_record failed"); vtable_set_null(attrs, name, VF_COPYNAME); // Failing would lead to acl termination success = 0; goto exit; } if (dbt_db_get(&hl->hl_dbt, lookup, &record)) { log_error("hitlist_lookup: dbt_db_get failed"); goto exit; } if (record == NULL) { // Add new record if (hl->hl_create) { log_debug("hitlist_lookup: %s add record", name); record = lookup; lookup = NULL; } // No record found, set symbol to null else { vtable_set_null(attrs, name, VF_COPYNAME); log_debug("hitlist_lookup: %s no record", name); success = 0; goto exit; } } else { update_record = 1; log_debug("hitlist_lookup: %s record found", name); } value = vlist_record_get(record, hl->hl_value_field); expire = vlist_record_get(record, hl->hl_expire_field); if (value == NULL || expire == NULL) { log_error("hitlist_lookup: vlist_record_get failed"); goto exit; } // Count if (hl->hl_count) { ++(*value); value_diff = 1; } // Sum else if (hl->hl_sum) { addition = vtable_lookup(attrs, hl->hl_sum); if (addition == NULL) { log_error("hitlist: sum field %s is undefined", hl->hl_sum); goto exit; } if (addition->v_type != VT_INT) { log_error("hitlist: sum field %s must be integer", hl->hl_sum); goto exit; } *value += *(VAR_INT_T *) addition->v_data; value_diff = *(VAR_INT_T *) addition->v_data; } if (hl->hl_update) { // Record never expires if (hl->hl_timeout == 0) { *expire = INT_MAX; expire_diff = 0; } // Record is extended every time a match is found else if (hl->hl_extend) { *expire = time(NULL) + hl->hl_timeout; expire_diff = hl->hl_timeout; } // Record expires after a fixed timeout else if (hl->hl_timeout && *expire == 0) { *expire = time(NULL) + hl->hl_timeout; expire_diff = hl->hl_timeout; } // SQL_SAFE_UPDATE if(update_record && hl->hl_dbt.dbt_driver->dd_use_sql) { if (hitlist_sql_safe_update(hl, record, hl->hl_value_field, value_diff, hl->hl_expire_field, expire_diff)) { log_error("hitlist_lookup: hitlist_sql_safe_update failed"); goto exit; } } else { if (dbt_db_set(&hl->hl_dbt, record)) { log_error("hitlist_lookup: dbt_db_set failed"); goto exit; } } } // Add symbol if (vtable_set_new(attrs, VT_INT, name, value, VF_COPY)) { log_error("hitlist_lookup: vtable_set_new failed"); goto exit; } success = 0; exit: if (lookup != NULL) { var_delete(lookup); } if (record != NULL) { var_delete(record); } if (pthread_mutex_unlock(&hl->hl_mutex)) { log_sys_error("hitlist_db_open: pthread_mutex_unlock"); } return success; }
static var_t * hitlist_record(hitlist_t *hl, var_t *attrs, int load_data) { ll_entry_t *pos; var_t *key; char *keystr; var_t *v; var_t *schema = NULL; VAR_INT_T zero = 0; char *name; name = hl->hl_table? hl->hl_table: hl->hl_name; schema = vlist_create(name, VF_KEEPNAME); if (schema == NULL) { log_error("hitlist_schema: vlist_create failed"); goto error; } pos = LL_START(hl->hl_keys); while ((key = ll_next(hl->hl_keys, &pos))) { // Impossible if (key->v_data == NULL) { log_error("hitlist_schema: key is NULL"); goto error; } // Bad configured if (key->v_type != VT_STRING) { log_error("hitlist_scheme: bad configuration %s:" " hitlist keys must be strings.", hl->hl_name); goto error; } keystr = key->v_data; // Variables if (keystr[0] == '$') { v = acl_variable_get(attrs, keystr); } // Regular symbol else { v = acl_symbol_get(attrs, keystr); } if (v == NULL) { log_error("hitlist_scheme: %s: lookup %s failed", hl->hl_name, keystr); goto error; } if (load_data && v->v_data == NULL) { log_error("hitlist_scheme: %s: key %s is NULL", hl->hl_name, keystr); goto error; } if (vlist_append_new(schema, v->v_type, keystr, load_data? v->v_data: NULL, VF_COPY | VF_KEY)) { log_error("hitlist_schema: %s: vlist_append_new" " failed", hl->hl_name); goto error; } } // Add value if (vlist_append_new(schema, VT_INT, hl->hl_value_field, load_data? &zero: NULL, VF_COPY)) { log_error("hitlist_schema: %s: vlist_append_new failed for %s", hl->hl_name, hl->hl_value_field); goto error; } if (vlist_append_new(schema, VT_INT, hl->hl_expire_field, load_data? &zero: NULL, VF_COPY)) { log_error("hitlist_schema: %s: vlist_append_new failed for %s", hl->hl_name, hl->hl_expire_field); goto error; } return schema; error: if (schema != NULL) { var_delete(schema); } return NULL; }
int spamd_query(milter_stage_t stage, char *name, var_t *attrs) { int sock = 0; var_t *symbols = NULL; int n; char recv_header[BUFLEN]; char buffer[BUFLEN]; char *p, *q; char *message = NULL; VAR_INT_T spam; VAR_FLOAT_T score; VAR_INT_T *message_size; long header_size, size; /* * Build received header */ header_size = spamd_header(attrs, recv_header, sizeof recv_header); if (header_size == -1) { log_error("spamd_query: spamd_header failed"); goto error; } log_debug("spamd_query: received header:\n%s", recv_header); /* * Get message size */ message_size = vtable_get(attrs, "message_size"); if (message_size == NULL) { log_error("spamd_query: vtable_get failed"); goto error; } size = header_size + *message_size; /* * Allocate message buffer */ message = (char *) malloc(size + 1); if (message == NULL) { log_sys_error("spamd_query: malloc"); goto error; } /* * Dump message */ memcpy(message, recv_header, header_size); if (milter_dump_message(message + header_size, size - header_size + 1, attrs) == -1) { log_error("spamd_query: milter_dump_message failed"); goto error; } snprintf(buffer, sizeof(buffer), "SYMBOLS SPAMC/1.2\r\n" "Content-length: %ld\r\n\r\n", size); sock = sock_connect_rr(&spamd_srr); if (sock == -1) { log_error("spamd_query: sock_connect failed"); goto error; } /* * Write spamassassin request */ if (write(sock, buffer, strlen(buffer)) == -1) { log_sys_error("spamd_query: write"); goto error; } /* * Write message */ if (write(sock, message, size) == -1) { log_sys_error("spamd_query: write"); goto error; } /* * Read response */ n = read(sock, buffer, sizeof buffer - 1); if (n == -1) { log_sys_error("spamd_query: read"); goto error; } buffer[n] = 0; /* * No answer. Seen when spamd ran out of mem. */ if (n == 0) { log_error("spamd_query: no data received"); goto error; } /* * Parse response */ p = buffer; if (strncmp(p, SPAMD_SPAMD, SPAMD_SPAMDLEN)) { spamd_printable_buffer(p, SPAMD_SPAMDLEN + 1); log_error("spamd_query: protocol error: expected='%s' " "received='%s'", SPAMD_SPAMD, p); goto error; } p += SPAMD_SPAMDLEN; if (strncmp(p, SPAMD_VERSION, SPAMD_VERSIONLEN)) { spamd_printable_buffer(p, SPAMD_VERSIONLEN + 1); log_notice("spamd_query: protocol version mismtach: " "expected='%s' received='%s'", SPAMD_VERSION, p); } p += SPAMD_VERSIONLEN; p = strstr(p, SPAMD_EX_OK); if(p == NULL) { log_error("spamd_query: spamd returned non EX_OK"); goto error; } /* * Spamd returns 2 lines. Read 2nd line if neccessary. */ p += SPAMD_EX_OKLEN; if(strlen(p) <= 2) { /* '\r\n' */ n = read(sock, buffer, sizeof(buffer)); if (n == -1) { log_sys_error("spamd_query: read"); goto error; } buffer[n] = 0; p = buffer; } else { p += 2; /* '\r\n' */ } /* * Parse results */ if(strncmp(p, SPAMD_SPAM, SPAMD_SPAMLEN)) { spamd_printable_buffer(p, SPAMD_SPAMLEN + 1); log_error("spamd_query: protocol error: expected='%s' " "received='%s'", SPAMD_SPAM, p); goto error; } p += SPAMD_SPAMLEN; if(!strncmp(p, SPAMD_TRUE, SPAMD_TRUELEN)) { spam = 1; p += SPAMD_TRUELEN; } else if(!strncmp(p, SPAMD_FALSE, SPAMD_FALSELEN)) { spam = 0; p += SPAMD_FALSELEN; } else { spamd_printable_buffer(p, (SPAMD_TRUELEN > SPAMD_FALSELEN ? SPAMD_TRUELEN: SPAMD_FALSELEN) + 1); log_error("spamd_query: protocol error: expected: '%s|%s' " "received='%s'", SPAMD_TRUE, SPAMD_FALSE, p); goto error; } /* * Cut score. */ q = strchr(p, ' '); if (q == NULL) { log_error("spamd_query: protocol error: couldn't find the " "next space character"); goto error; } *q++ = 0; score = (VAR_FLOAT_T) strtod(p, NULL); /* * Set SYMBOLS */ p = strchr(q, '\r'); if (p == NULL) { log_error("spamd_query: protocol error: couldn't find the " "start of the spamd symbols"); goto error; } p += 4; /* \r\n\r\n */ q = strchr(p, '\r'); if (q == NULL) { log_error("spamd_query: protocol error: couldn't find the " "end of the spamd symbols"); goto error; } *q = 0; log_message(LOG_ERR, attrs, "spamd: spam=%d score=%.1f symbols=%s", spam, score, p); symbols = var_create(VT_LIST, "spamd_symbols", NULL, VF_KEEPNAME | VF_CREATE); if (symbols == NULL) { log_error("spamd_query: var_create failed"); goto error; } do { q = strchr(p, ','); if (q) { *q = 0; } if (vlist_append_new(symbols, VT_STRING, NULL, p, VF_COPYDATA) == -1) { log_error("spamd_query: vlist_append failed"); goto error; } p = q + 1; } while (q); if (vtable_setv(attrs, VT_INT, "spamd_spam", &spam, VF_KEEPNAME | VF_COPYDATA, VT_FLOAT, "spamd_score", &score, VF_KEEPNAME | VF_COPYDATA, VT_NULL)) { log_error("spamd_query: vtable_setv failed"); goto error; } if (vtable_set(attrs, symbols)) { log_error("spamd_query: vtable_set failed"); goto error; } close(sock); free(message); return 0; error: if (message) { free(message); } if (sock > 0) { close(sock); } if (symbols) { var_delete(symbols); } return -1; }