/* * _hostrange_output * * Output metric data in hostrange format. The algorithm involves * using the metric_value as a hash key. Each hash item will then * store the hosts with the same metric_value/key. */ static void _hostrange_output(List l) { #if CEREBRO_DEBUG const char *func = __FUNCTION__; #endif /* CEREBRO_DEBUG */ struct node_metric_data *data = NULL; ListIterator litr = NULL; unsigned int count; hash_t h; assert(l); count = List_count(l); #if CEREBRO_DEBUG if (!count) err_exit("%s: invalid count", func); #endif /* CEREBRO_DEBUG */ h = Hash_create(count, (hash_key_f)hash_key_string, (hash_cmp_f)strcmp, (hash_del_f)_hostrange_data_destroy); litr = List_iterator_create(l); while ((data = list_next(litr))) { char buf[CEREBRO_STAT_BUFLEN]; struct hostrange_data *hd; _metric_value_str(data->metric_value_type, data->metric_value_len, data->metric_value, buf, CEREBRO_STAT_BUFLEN); if (!(hd = Hash_find(h, buf))) { hd = Malloc(sizeof(struct hostrange_data)); hd->hl = Hostlist_create(NULL); hd->key = Strdup(buf); Hash_insert(h, hd->key, hd); } Hostlist_push(hd->hl, data->nodename); } Hash_for_each(h, _hostrange_output_data, NULL); /* No need to destroy list iterator, caller will destroy List */ Hash_destroy(h); }
/* Функция корректного выхода. */ void correct_exit(){ for(int i = 0; i < server_pool.threads_count; i++){ pthread_cancel(server_pool.tids[i]); } zmq_close(server_pool.workers); zmq_close(server_pool.clients); zmq_ctx_destroy(server_pool.context); free(server_pool.tids); Cache_destroy(&server_pool.cache); Hash_destroy(&server_pool.hash); if(server_pool.be_verbose) syslog(LOG_INFO, "Server exit."); closelog(); exit(EXIT_SUCCESS); }
extern void Config_section_destroy(Config_section_T *section) { if(section == NULL || *section == NULL) return; if((*section)->name) { free((*section)->name); (*section)->name = NULL; } Hash_destroy(&((*section)->vars)); free(*section); *section = NULL; }
/* * _setup_event_node_timeout_data * * Setup event timeout calculation data structures. * * Return 0 on success, -1 on error */ static int _setup_event_node_timeout_data(void) { struct timeval tv; int num; assert(conf.event_server); assert(listener_data); event_node_timeout_data = List_create(NULL); event_node_timeout_data_index = Hash_create(EVENT_NODE_TIMEOUT_SIZE_DEFAULT, (hash_key_f)hash_key_string, (hash_cmp_f)strcmp, (hash_del_f)NULL); event_node_timeout_data_index_size = EVENT_NODE_TIMEOUT_SIZE_DEFAULT; Gettimeofday(&tv, NULL); num = Hash_for_each(listener_data, _event_node_timeout_data_callback, &(tv.tv_sec)); if (num != listener_data_numnodes) { fprintf(stderr, "* invalid create count: num=%d numnodes=%d\n", num, listener_data_numnodes); goto cleanup; } return 0; cleanup: if (event_node_timeout_data) { List_destroy(event_node_timeout_data); event_node_timeout_data = NULL; } if (event_node_timeout_data_index) { Hash_destroy(event_node_timeout_data_index); event_node_timeout_data_index = NULL; } return -1; }
void cerebrod_rehash(hash_t *old_hash, int *hash_size, int hash_size_increment, int hash_num, pthread_mutex_t *hash_mutex) { hash_t new_hash; assert(old_hash && hash_size && hash_size_increment && hash_num); #if CEREBRO_DEBUG /* Should be called with lock already set */ if (hash_mutex) { int rv = Pthread_mutex_trylock(hash_mutex); if (rv != EBUSY) CEREBROD_EXIT(("mutex not locked: rv=%d", rv)); } #endif /* CEREBRO_DEBUG */ *hash_size += hash_size_increment; new_hash = Hash_create(*hash_size, (hash_key_f)hash_key_string, (hash_cmp_f)strcmp, (hash_del_f)_Free); if (Hash_for_each(*old_hash, _hash_reinsert, &new_hash) != hash_num) CEREBROD_EXIT(("invalid reinsert: hash_num=%d", hash_num)); if (Hash_remove_if(*old_hash, _hash_removeall, NULL) != hash_num) CEREBROD_EXIT(("invalid removeall: hash_num=%d", hash_num)); Hash_destroy(*old_hash); *old_hash = new_hash; }
/* * Under almost any circumstance, don't return a -1 error, cerebro can * go on without loading monitor modules. The listener_data_init_lock * should already be set. */ int cerebrod_event_modules_setup(void) { int i, event_module_count, event_index_len, event_index_count = 0; struct cerebrod_event_module_list *el = NULL; #if CEREBRO_DEBUG int rv; #endif /* CEREBRO_DEBUG */ assert(listener_data); #if CEREBRO_DEBUG /* Should be called with lock already set */ rv = Pthread_mutex_trylock(&listener_data_init_lock); if (rv != EBUSY) CEREBROD_EXIT(("mutex not locked: rv=%d", rv)); #endif /* CEREBRO_DEBUG */ if (!conf.event_server) return 0; if (!(event_handle = event_modules_load())) { CEREBROD_DBG(("event_modules_load")); goto cleanup; } if ((event_module_count = event_modules_count(event_handle)) < 0) { CEREBROD_DBG(("event_modules_count")); goto cleanup; } if (!event_module_count) { if (conf.debug && conf.event_server_debug) { Pthread_mutex_lock(&debug_output_mutex); fprintf(stderr, "**************************************\n"); fprintf(stderr, "* No Event Modules Found\n"); fprintf(stderr, "**************************************\n"); Pthread_mutex_unlock(&debug_output_mutex); } goto cleanup; } /* Each event module may want multiple metrics and/or offer multiple * event names. We'll assume there will never be more than 2 per * event module, and that will be enough to avoid all hash * collisions. */ event_index_len = event_module_count * 2; event_index = Hash_create(event_index_len, (hash_key_f)hash_key_string, (hash_cmp_f)strcmp, (hash_del_f)_cerebrod_event_module_list_destroy); event_names = List_create((ListDelF)_Free); event_module_timeouts = List_create((ListDelF)_cerebrod_event_module_timeout_data_destroy); event_module_timeout_index = Hash_create(event_module_count, (hash_key_f)hash_key_string, (hash_cmp_f)strcmp, (hash_del_f)list_destroy); for (i = 0; i < event_module_count; i++) { struct cerebrod_event_module *event_module; char *module_name, *module_metric_names, *module_event_names; char *metricPtr, *metricbuf; char *eventnamePtr, *eventbuf; int timeout; module_name = event_module_name(event_handle, i); if (conf.event_module_exclude_len) { int found_exclude = 0; int j; for (j = 0; j < conf.event_module_exclude_len; j++) { if (!strcasecmp(conf.event_module_exclude[j], module_name)) { found_exclude++; break; } } if (found_exclude) { if (conf.debug && conf.event_server_debug) { Pthread_mutex_lock(&debug_output_mutex); fprintf(stderr, "**************************************\n"); fprintf(stderr, "* Skip Event Module: %s\n", module_name); fprintf(stderr, "**************************************\n"); Pthread_mutex_unlock(&debug_output_mutex); } CEREBROD_ERR(("Dropping event module: %s", module_name)); continue; } } if (conf.debug && conf.event_server_debug) { Pthread_mutex_lock(&debug_output_mutex); fprintf(stderr, "**************************************\n"); fprintf(stderr, "* Setup Event Module: %s\n", module_name); fprintf(stderr, "**************************************\n"); Pthread_mutex_unlock(&debug_output_mutex); } if (event_module_setup(event_handle, i) < 0) { CEREBROD_DBG(("event_module_setup failed: %s", module_name)); continue; } if (!(module_metric_names = event_module_metric_names(event_handle, i)) < 0) { CEREBROD_DBG(("event_module_metric_names failed: %s", module_name)); event_module_cleanup(event_handle, i); continue; } if (!(module_event_names = event_module_event_names(event_handle, i)) < 0) { CEREBROD_DBG(("event_module_event_names failed: %s", module_name)); event_module_cleanup(event_handle, i); continue; } if ((timeout = event_module_timeout_length(event_handle, i)) < 0) { CEREBROD_DBG(("event_module_timeout_length failed: %s", module_name)); event_module_cleanup(event_handle, i); continue; } event_module = Malloc(sizeof(struct cerebrod_event_module_info)); event_module->metric_names = Strdup(module_metric_names); event_module->event_names = Strdup(module_event_names); event_module->index = i; Pthread_mutex_init(&(event_module->event_lock), NULL); /* The monitoring module may support multiple metrics */ metricPtr = strtok_r(event_module->metric_names, ",", &metricbuf); while (metricPtr) { if (!(el = Hash_find(event_index, metricPtr))) { el = (struct cerebrod_event_module_list *)Malloc(sizeof(struct cerebrod_event_module_list)); el->event_list = List_create((ListDelF)_cerebrod_event_module_info_destroy); Pthread_mutex_init(&(el->event_list_lock), NULL); List_append(el->event_list, event_module); Hash_insert(event_index, metricPtr, el); event_index_count++; } else List_append(el->event_list, event_module); metricPtr = strtok_r(NULL, ",", &metricbuf); } /* The monitoring module may support multiple event names */ eventnamePtr = strtok_r(event_module->event_names, ",", &eventbuf); while (eventnamePtr) { if (!list_find_first(event_names, (ListFindF)_cerebrod_name_strcmp, eventnamePtr)) { List_append(event_names, eventnamePtr); if (conf.debug && conf.event_server_debug) { Pthread_mutex_lock(&debug_output_mutex); fprintf(stderr, "**************************************\n"); fprintf(stderr, "* Event Name: %s\n", eventnamePtr); fprintf(stderr, "**************************************\n"); Pthread_mutex_unlock(&debug_output_mutex); } } eventnamePtr = strtok_r(NULL, ",", &eventbuf); } if (timeout) { struct cerebrod_event_module_timeout_data *mtd; List modules_list; if (!(mtd = List_find_first(event_module_timeouts, _event_module_timeout_data_find_callback, &timeout))) { char strbuf[64]; mtd = (struct cerebrod_event_module_timeout_data *)Malloc(sizeof(struct cerebrod_event_module_timeout_data)); mtd->timeout = timeout; snprintf(strbuf, 64, "%d", timeout); mtd->timeout_str = Strdup(strbuf); List_append(event_module_timeouts, mtd); if (timeout < event_module_timeout_min) event_module_timeout_min = timeout; } if (!(modules_list = Hash_find(event_module_timeout_index, mtd->timeout_str))) { modules_list = List_create((ListDelF)NULL); List_append(modules_list, event_module); Hash_insert(event_module_timeout_index, mtd->timeout_str, modules_list); } else List_append(modules_list, event_module); } } List_sort(event_module_timeouts, _event_module_timeout_data_compare); if (!event_index_count) goto cleanup; if (_setup_event_node_timeout_data() < 0) goto cleanup; /* * Since the cerebrod listener is started before any of the event * threads (node_timeout, queue_monitor, server), this must be * created in here (which is called by the listener) to avoid a * possible race of modules creating events before the event_queue * is created. */ event_queue = List_create((ListDelF)cerebrod_event_to_send_destroy); return 1; cleanup: if (event_handle) { event_modules_unload(event_handle); event_handle = NULL; } if (event_index) { Hash_destroy(event_index); event_index = NULL; } if (event_names) { List_destroy(event_names); event_names = NULL; } return 0; }
/* * Under almost any circumstance, don't return a -1 error, cerebro can * go on without loading monitor modules. The listener_data_init_lock * should already be set. */ int cerebrod_monitor_modules_setup(void) { int i, monitor_module_count, monitor_index_len, monitor_index_count = 0; struct cerebrod_monitor_module_list *ml = NULL; #if CEREBRO_DEBUG int rv; #endif /* CEREBRO_DEBUG */ #if CEREBRO_DEBUG /* Should be called with lock already set */ rv = Pthread_mutex_trylock(&listener_data_init_lock); if (rv != EBUSY) CEREBROD_EXIT(("mutex not locked: rv=%d", rv)); #endif /* CEREBRO_DEBUG */ if (!(monitor_handle = monitor_modules_load())) { CEREBROD_DBG(("monitor_modules_load")); goto cleanup; } if ((monitor_module_count = monitor_modules_count(monitor_handle)) < 0) { CEREBROD_DBG(("monitor_modules_count")); goto cleanup; } if (!monitor_module_count) { if (conf.debug && conf.listen_debug) { Pthread_mutex_lock(&debug_output_mutex); fprintf(stderr, "**************************************\n"); fprintf(stderr, "* No Monitor Modules Found\n"); fprintf(stderr, "**************************************\n"); Pthread_mutex_unlock(&debug_output_mutex); } goto cleanup; } /* Each monitor module may wish to monitor multiple metrics. We'll * assume there will never be more than 2 metrics per monitor module, and * that will be enough to avoid all hash collisions. */ monitor_index_len = monitor_module_count * 2; monitor_index = Hash_create(monitor_index_len, (hash_key_f)hash_key_string, (hash_cmp_f)strcmp, (hash_del_f)_cerebrod_monitor_module_list_destroy); for (i = 0; i < monitor_module_count; i++) { struct cerebrod_monitor_module_info *monitor_module; char *module_name, *metric_names; char *metricPtr, *metricbuf; module_name = monitor_module_name(monitor_handle, i); if (conf.monitor_module_exclude_len) { int found_exclude = 0; int j; for (j = 0; j < conf.monitor_module_exclude_len; j++) { if (!strcasecmp(conf.monitor_module_exclude[j], module_name)) { found_exclude++; break; } } if (found_exclude) { if (conf.debug && conf.listen_debug) { Pthread_mutex_lock(&debug_output_mutex); fprintf(stderr, "**************************************\n"); fprintf(stderr, "* Skip Monitor Module: %s\n", module_name); fprintf(stderr, "**************************************\n"); Pthread_mutex_unlock(&debug_output_mutex); } CEREBROD_ERR(("Dropping monitor module: %s", module_name)); continue; } } if (conf.debug && conf.listen_debug) { Pthread_mutex_lock(&debug_output_mutex); fprintf(stderr, "**************************************\n"); fprintf(stderr, "* Setup Monitor Module: %s\n", module_name); fprintf(stderr, "**************************************\n"); Pthread_mutex_unlock(&debug_output_mutex); } if (monitor_module_setup(monitor_handle, i) < 0) { CEREBROD_DBG(("monitor_module_setup failed: %s", module_name)); continue; } if (!(metric_names = monitor_module_metric_names(monitor_handle, i)) < 0) { CEREBROD_DBG(("monitor_module_metric_names failed: %s", module_name)); monitor_module_cleanup(monitor_handle, i); continue; } monitor_module = Malloc(sizeof(struct cerebrod_monitor_module_info)); monitor_module->metric_names = Strdup(metric_names); monitor_module->index = i; Pthread_mutex_init(&(monitor_module->monitor_lock), NULL); /* The monitoring module may support multiple metrics */ metricPtr = strtok_r(monitor_module->metric_names, ",", &metricbuf); while (metricPtr) { if (!(ml = Hash_find(monitor_index, metricPtr))) { ml = (struct cerebrod_monitor_module_list *)Malloc(sizeof(struct cerebrod_monitor_module_list)); ml->monitor_list = List_create((ListDelF)_cerebrod_monitor_module_info_destroy); Pthread_mutex_init(&(ml->monitor_list_lock), NULL); List_append(ml->monitor_list, monitor_module); Hash_insert(monitor_index, metricPtr, ml); monitor_index_count++; } else List_append(ml->monitor_list, monitor_module); metricPtr = strtok_r(NULL, ",", &metricbuf); } } if (!monitor_index_count) goto cleanup; return 1; cleanup: if (monitor_handle) { monitor_modules_unload(monitor_handle); monitor_handle = NULL; } if (monitor_index) { Hash_destroy(monitor_index); monitor_index = NULL; } return 0; }
int main(void) { Lexer_source_T ls; Lexer_T l; Config_parser_T cp; Config_T c; struct Greyd_state gs; struct Con con; Blacklist_T bl1, bl2, bl3; struct sockaddr_storage src; int ret; char *conf = "hostname = \"greyd.org\"\n" "banner = \"greyd IP-based SPAM blocker\"\n" "section grey {\n" " enable = 1,\n" " traplist_name = \"test traplist\",\n" " traplist_message = \"you have been trapped\",\n" " grey_expiry = 3600,\n" " stutter = 15\n" "}\n" "section firewall {\n" " driver = \"../drivers/fw_dummy.so\"\n" "}\n" "section database {\n" " driver = \"../drivers/bdb.so\",\n" " path = \"/tmp/greyd_test_grey.db\"\n" "}"; /* Empty existing database file. */ ret = unlink("/tmp/greyd_test_grey.db"); if(ret < 0 && errno != ENOENT) { printf("Error unlinking test Berkeley DB: %s\n", strerror(errno)); } TEST_START(49); c = Config_create(); ls = Lexer_source_create_from_str(conf, strlen(conf)); l = Config_lexer_create(ls); cp = Config_parser_create(l); Config_parser_start(cp, c); /* * Set up the greyd state and blacklists. */ memset(&gs, 0, sizeof(gs)); gs.config = c; gs.max_cons = 4; gs.max_black = 4; gs.blacklists = Hash_create(5, NULL); bl1 = Blacklist_create("blacklist_1", "You (%A) are on blacklist 1", BL_STORAGE_TRIE); bl2 = Blacklist_create("blacklist_2", "You (%A) are on blacklist 2", BL_STORAGE_TRIE); bl3 = Blacklist_create("blacklist_3_with_an_enormously_big_long_long_epic_epicly_long_large_name", "Your address %A\\nis on blacklist 3", BL_STORAGE_TRIE); Hash_insert(gs.blacklists, bl1->name, bl1); Hash_insert(gs.blacklists, bl2->name, bl2); Hash_insert(gs.blacklists, bl3->name, bl3); Blacklist_add(bl1, "10.10.10.1/32"); Blacklist_add(bl1, "10.10.10.2/32"); Blacklist_add(bl2, "10.10.10.1/32"); Blacklist_add(bl2, "10.10.10.2/32"); Blacklist_add(bl2, "2001::fad3:1/128"); Blacklist_add(bl3, "10.10.10.2/32"); Blacklist_add(bl3, "10.10.10.3/32"); Blacklist_add(bl3, "2001::fad3:1/128"); /* * Start testing the connection management. */ memset(&src, 0, sizeof(src)); ((struct sockaddr_in *) &src)->sin_family = AF_INET; inet_pton(AF_INET, "10.10.10.1", &((struct sockaddr_in *) &src)->sin_addr); memset(&con, 0, sizeof(con)); Con_init(&con, 0, &src, &gs); TEST_OK(con.state == 0, "init state ok"); TEST_OK(con.last_state == 0, "last state ok"); TEST_OK(List_size(con.blacklists) == 2, "blacklist matches ok"); TEST_OK(!strcmp(con.src_addr, "10.10.10.1"), "src addr ok"); TEST_OK(con.out_buf != NULL, "out buf ok"); TEST_OK(con.out_p == con.out_buf, "out buf pointer ok"); TEST_OK(con.out_size == CON_OUT_BUF_SIZE, "out buf size ok"); TEST_OK(!strcmp(con.lists, "blacklist_1 blacklist_2"), "list summary ok"); /* The size of the banner. */ TEST_OK(con.out_remaining == 75, "out buf remaining ok"); TEST_OK(gs.clients == 1, "clients ok"); TEST_OK(gs.black_clients == 1, "blacklisted clients ok"); /* * Test the closing of a connection. */ Con_close(&con, &gs); TEST_OK(List_size(con.blacklists) == 0, "blacklist empty ok"); TEST_OK(con.out_buf == NULL, "out buf ok"); TEST_OK(con.out_p == NULL, "out buf pointer ok"); TEST_OK(con.out_size == 0, "out buf size ok"); TEST_OK(con.lists == NULL, "lists ok"); TEST_OK(gs.clients == 0, "clients ok"); TEST_OK(gs.black_clients == 0, "blacklisted clients ok"); /* Test recycling a connection. */ memset(&src, 0, sizeof(src)); ((struct sockaddr_in6 *) &src)->sin6_family = AF_INET6; inet_pton(AF_INET6, "2001::fad3:1", &((struct sockaddr_in6 *) &src)->sin6_addr); Con_init(&con, 0, &src, &gs); TEST_OK(con.state == 0, "init state ok"); TEST_OK(con.last_state == 0, "last state ok"); TEST_OK(List_size(con.blacklists) == 2, "blacklist matches ok"); TEST_OK(!strcmp(con.src_addr, "2001::fad3:1"), "src addr ok"); TEST_OK(con.out_buf != NULL, "out buf ok"); TEST_OK(con.out_p == con.out_buf, "out buf pointer ok"); TEST_OK(con.out_size == CON_OUT_BUF_SIZE, "out buf size ok"); /* * As the 3rd blacklist's name is really long, the summarize lists * function should truncate with a "...". */ TEST_OK(!strcmp(con.lists, "blacklist_2 ..."), "list summary ok"); /* The size of the banner. */ TEST_OK(con.out_remaining == 75, "out buf remaining ok"); TEST_OK(gs.clients == 1, "clients ok"); TEST_OK(gs.black_clients == 1, "blacklisted clients ok"); /* * Test the rejection message building. */ Con_build_reply(&con, "451"); TEST_OK(!strcmp(con.out_p, "451-You (2001::fad3:1) are on blacklist 2\n" "451-Your address 2001::fad3:1\n" "451 is on blacklist 3\n"), "Blacklisted error response ok"); TEST_OK(con.out_remaining == 94, "out buf remaining ok"); /* * Test the writing of the buffer without stuttering. */ time_t now = time(NULL); int con_pipe[2], to_write, nread; char in[CON_OUT_BUF_SIZE]; pipe(con_pipe); con.fd = con_pipe[1]; con.w = now; to_write = con.out_remaining; Con_handle_write(&con, &now, &gs); nread = read(con_pipe[0], in, to_write); in[nread] = '\0'; TEST_OK(!strcmp(in, "451-You (2001::fad3:1) are on blacklist 2\n" "451-Your address 2001::fad3:1\n" "451 is on blacklist 3\n"), "Con write without stuttering ok"); /* * Test the writing with stuttering. Note the reply is longer due * to the stutering adding in \r before each \n if there isn't one * already. */ Con_build_reply(&con, "451"); gs.max_cons = 100; gs.max_black = 100; con.w = now; while(con.out_remaining > 0) { Con_handle_write(&con, &now, &gs); now += con.stutter + 1; } memset(in, 0, sizeof(in)); nread = read(con_pipe[0], in, to_write + 3); in[nread] = '\0'; TEST_OK(!strcmp(in, "451-You (2001::fad3:1) are on blacklist 2\r\n" "451-Your address 2001::fad3:1\r\n" "451 is on blacklist 3\r\n"), "Con write with stuttering ok"); /* Test recycling a connection, which is not on a blacklist. */ Con_close(&con, &gs); memset(&src, 0, sizeof(src)); ((struct sockaddr_in6 *) &src)->sin6_family = AF_INET6; inet_pton(AF_INET6, "fa40::fad3:1", &((struct sockaddr_in6 *) &src)->sin6_addr); Con_init(&con, 0, &src, &gs); TEST_OK(List_size(con.blacklists) == 0, "not on blacklist ok"); /* * Note custom error codes only apply for blacklist connections, so expect * a 451 for this greylisted connections. */ Con_build_reply(&con, "551"); TEST_OK(!strcmp(con.out_p, "451 Temporary failure, please try again later.\r\n"), "greylisted error response ok"); Con_close(&con, &gs); List_destroy(&con.blacklists); /* * Test the connection reading function. */ memset(&src, 0, sizeof(src)); ((struct sockaddr_in *) &src)->sin_family = AF_INET; inet_pton(AF_INET, "10.10.10.1", &((struct sockaddr_in *) &src)->sin_addr); pipe(con_pipe); memset(&con, 0, sizeof(con)); Con_init(&con, con_pipe[0], &src, &gs); char *out = "EHLO greyd.org\r\n"; write(con_pipe[1], out, strlen(out)); Con_handle_read(&con, &now, &gs); /* This should change the state. */ TEST_OK(con.state == CON_STATE_HELO_IN, "Initial state set ok"); Con_handle_read(&con, &now, &gs); TEST_OK(!strcmp(con.in_buf, "EHLO greyd.org"), "con read ok"); TEST_OK(!strcmp(con.helo, "greyd.org"), "helo parsed ok"); TEST_OK(con.state = CON_STATE_HELO_OUT, "state helo out ok"); char *mail_from = "MAIL FROM: <*****@*****.**>\r\n"; write(con_pipe[1], mail_from, strlen(mail_from)); Con_next_state(&con, &now, &gs); TEST_OK(con.state = CON_STATE_MAIL_IN, "state mail in ok"); Con_handle_read(&con, &now, &gs); TEST_OK(!strcmp(con.mail, "*****@*****.**"), "MAIL FROM parsed ok"); TEST_OK(con.state = CON_STATE_MAIL_OUT, "state mail out ok"); char *rcpt = "RCPT TO: [email protected]\r\n"; write(con_pipe[1], rcpt, strlen(rcpt)); Con_next_state(&con, &now, &gs); TEST_OK(con.state = CON_STATE_RCPT_IN, "state rcpt in ok"); Con_handle_read(&con, &now, &gs); TEST_OK(!strcmp(con.rcpt, "*****@*****.**"), "RCPT parsed ok"); TEST_OK(con.state = CON_STATE_RCPT_OUT, "state rcpt out ok"); char *data = "DATA\r\n"; write(con_pipe[1], data, strlen(data)); Con_next_state(&con, &now, &gs); TEST_OK(con.state = CON_STATE_RCPT_IN, "state rcpt in ok"); /* this will goto spam. */ Con_handle_read(&con, &now, &gs); TEST_OK(con.state = CON_STATE_DATA_OUT, "state data out ok"); char *msg = "This is a spam message\r\ndeliver me!\r\n.\r\n"; write(con_pipe[1], msg, strlen(msg)); Con_next_state(&con, &now, &gs); TEST_OK(con.state = CON_STATE_MESSAGE, "state message ok"); Con_handle_read(&con, &now, &gs); TEST_OK(con.state = CON_STATE_CLOSE, "state close ok"); /* This should close the connection. */ Con_next_state(&con, &now, &gs); /* Cleanup. */ List_destroy(&con.blacklists); Hash_destroy(&gs.blacklists); Blacklist_destroy(&bl1); Blacklist_destroy(&bl2); Blacklist_destroy(&bl3); Config_destroy(&c); Config_parser_destroy(&cp); TEST_COMPLETE; }