/* * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: t-edbr-1.c,v 1.38 2007/06/18 04:42:30 ca Exp $") #include "sm/assert.h" #include "sm/magic.h" #include "sm/error.h" #include "sm/memops.h" #include "sm/io.h" #include "sm/test.h" #include "sm/bhtable.h" #include "sm/edb.h" #include "sm/actdb-int.h" #include "sm/regex.h" #define QMGR_DEBUG_DEFINE 1 #include "sm/qmgrdbg.h" /* ** Print and check content of DEFEDB. ** This is almost a "mailq" command. */ /* Hack ... */ #define RCPT_MAX_LEN 256 #define MAX_STRLEN 1024 #define ERRBUF_SIZE 1024 #define MAX_MATCH 256 #define T_BHT_SIZE 1023 #define TA_TYPE 'T' #define RCPT_TYPE 'R' #define BOTH_TYPE 'B' /* seen both entries */ static int Verbose; static regex_t IdReg; static regex_t AddrReg; static bool IdPat; static bool AddrPat; static int b_regmatch(const regex_t *preg, const char *string, int eflags) { int r; size_t nmatch; regmatch_t pmatch[MAX_MATCH]; nmatch = 0; r = regexec(preg, string, nmatch, pmatch, eflags); return r; } static aq_rcpt_P new_rcpt(void) { aq_rcpt_P aq_rcpt; aq_rcpt = (aq_rcpt_P) sm_zalloc(sizeof(*aq_rcpt)); SM_TEST(aq_rcpt != NULL); if (aq_rcpt == NULL) return NULL; #if AQ_RCPT_CHECK aq_rcpt->sm_magic = SM_AQ_RCPT_MAGIC; #endif /* AQ_TA_CHECK */ return aq_rcpt; } static sm_ret_T aq_rcpt_clr(aq_rcpt_P aq_rcpt) { if (aq_rcpt == NULL) return SM_SUCCESS; if (aq_rcpt->aqr_addrs != NULL && !AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR) && aq_rcpt->aqr_addrs != &(aq_rcpt->aqr_addr_mf)) SM_FREE(aq_rcpt->aqr_addrs); SM_STR_FREE(aq_rcpt->aqr_pa); SM_STR_FREE(aq_rcpt->aqr_msg); SM_STR_FREE(aq_rcpt->aqr_dsn_msg); SM_FREE(aq_rcpt->aqr_dsns); /* clear entire struct? */ aq_rcpt->aqr_err_st = 0; aq_rcpt->aqr_addr_fail = 0; aq_rcpt->aqr_dsn_idx = 0; return SM_SUCCESS; } static void printtime(time_T t, char *name) { if (Verbose > 1) sm_io_fprintf(smioout, "\t%s=%ld %s", name, (long) t, ctime(&t)); else sm_io_fprintf(smioout, "\t%s=%ld\n", name, (long) t); } static aq_ta_P new_ta(void) { aq_ta_P aq_ta; aq_ta = (aq_ta_P) sm_zalloc(sizeof(*aq_ta)); SM_TEST(aq_ta != NULL); if (aq_ta == NULL) return NULL; aq_ta->aqt_mail = (aq_mail_P) sm_zalloc(sizeof(*(aq_ta->aqt_mail))); SM_TEST(aq_ta->aqt_mail != NULL); if (aq_ta->aqt_mail == NULL) return NULL; #if AQ_TA_CHECK aq_ta->sm_magic = SM_AQ_TA_MAGIC; #endif /* AQ_TA_CHECK */ return aq_ta; } /* ARGSUSED1 */ static void free_fn(void *str, void *key, void *ctx) { (void) key; (void) ctx; if (str != NULL) sm_free(str); } /* ARGSUSED0 */ static void free_aqs(void *aq, void *key, void *ctx) { /* memory leak ... */ /* should free aq */ (void) aq; (void) key; (void) ctx; } /* ARGSUSED1 */ static sm_ret_T walk_fn(bht_entry_P be, void *ctx) { char *type; (void) ctx; type = (char *) be->bhe_value; SM_TEST(type != NULL); if (type == NULL) return sm_err_perm(ENOENT); SM_TEST(*type == BOTH_TYPE); if (*type != BOTH_TYPE) { if (Verbose > 0) sm_io_fprintf(smioerr, "type=%c, id=%s\n", *type, be->bhe_key); return sm_err_perm(ENOENT); } return SM_SUCCESS; } /* ARGSUSED1 */ static sm_ret_T walk_ta(bht_entry_P be, void *ctx) { aq_ta_P aq_ta; (void) ctx; aq_ta = (aq_ta_P) be->bhe_value; SM_TEST(aq_ta != NULL); if (aq_ta == NULL) return sm_err_perm(ENOENT); SM_TEST(aq_ta->aqt_rcpts_inaq == 0); if (aq_ta->aqt_rcpts_inaq != 0) { if (Verbose > 0) sm_io_fprintf(smioerr, "ta=%s, rcpts_missing=%d\n", aq_ta->aqt_ss_ta_id, aq_ta->aqt_rcpts_inaq); } return SM_SUCCESS; } /* add some logic to check counters, e.g., tried, total, left, ... */ static bool lookup(bht_P tn, char *id, size_t len, char type) { sm_ret_T ret; char *str, *str2; bht_entry_P bi; str = bht_find(tn, id, len); if (str == NULL) { str = sm_malloc(len); SM_TEST(str != NULL); if (str == NULL) return false; str2 = sm_malloc(1); SM_TEST(str2 != NULL); if (str2 == NULL) return false; *str2 = type; ret = bht_add(tn, id, len, str2, &bi); SM_TEST(ret == SM_SUCCESS); SM_TEST(bi != NULL); } else { if (type == TA_TYPE) { /* There can be only one.... transaction */ if (*str == type) { SM_TEST(*str != type); return false; } } /* If it's a different type (TA/RCPT) then set it to BOTH */ if (*str != type) *str = BOTH_TYPE; } return true; } static void edbr1(uint flags) { sm_ret_T ret, rt; uint u; bool ok; edb_ctx_P edb_ctx; edb_cnf_T edb_cnf; aq_ctx_P aq_ctx; aq_ctx_T aq_ctx_s; aq_rcpt_P aq_rcpt; aq_ta_P aq_ta, aq_ta_f; edb_req_P edb_req; edb_cursor_P edb_cursor; bht_P tn, t_aq; bht_entry_P bi; aq_rcpt = NULL; aq_ta = NULL; edb_req = NULL; tn = NULL; t_aq = NULL; edb_cursor = NULL; edb_ctx = NULL; aq_ctx = &aq_ctx_s; sm_memzero(&edb_cnf, sizeof(edb_cnf)); edb_cnf.edbcnf_oflags = flags; ret = edb_open(&edb_cnf, NULL, NULL, &edb_ctx); SM_TEST(ret == SM_SUCCESS); if (sm_is_err(ret)) { if (Verbose > 0) sm_io_fprintf(smioerr, "edb_open=%r\n", ret); return; } ret = aq_open(NULL, &aq_ctx, 256, 0); SM_TEST(ret == SM_SUCCESS); if (sm_is_err(ret)) goto error; aq_rcpt = new_rcpt(); SM_TEST(aq_rcpt != NULL); if (aq_rcpt == NULL) goto error; ret = edb_req_new(edb_ctx, EDB_RQF_NONE, &edb_req, THR_NO_LOCK); SM_TEST(ret == SM_SUCCESS); if (sm_is_err(ret)) goto error; tn = bht_new(T_BHT_SIZE, T_BHT_SIZE * 2); SM_TEST(tn != NULL); if (tn == NULL) goto error; t_aq = bht_new(T_BHT_SIZE, T_BHT_SIZE * 2); SM_TEST(t_aq != NULL); if (t_aq == NULL) goto error; ret = edb_rd_open(edb_ctx, &edb_cursor); SM_TEST(ret == SM_SUCCESS); if (sm_is_err(ret)) goto error; for (;;) { ret = edb_rd_next(edb_ctx, edb_cursor, edb_req); if (ret == sm_error_perm(SM_EM_EDB, DB_NOTFOUND)) break; SM_TEST(ret == SM_SUCCESS); if (sm_is_err(ret)) goto error; rt = edb_get_type(edb_req); if (sm_is_err(rt)) goto error; if (rt == EDB_REQ_TA) { aq_ta = new_ta(); SM_TEST(aq_ta != NULL); if (aq_ta == NULL) goto error; ret = edb_ta_dec(edb_req, aq_ta); SM_TEST(ret == SM_SUCCESS); if (sm_is_err(ret)) goto error; aq_ta->aqt_ss_ta_id[SMTP_STID_SIZE - 1] = '\0'; if (IdPat && b_regmatch(&IdReg, aq_ta->aqt_ss_ta_id, 0) != 0) continue; if (AddrPat && b_regmatch(&AddrReg, (const char *) sm_str_getdata(aq_ta->aqt_mail->aqm_pa), 0) != 0) continue; sm_io_fprintf(smioout, "got transaction\n"); sm_io_fprintf(smioout, "\tta_id=%s\n", aq_ta->aqt_ss_ta_id); sm_io_fprintf(smioout, "\tmail=%S\n", aq_ta->aqt_mail->aqm_pa); printtime(aq_ta->aqt_st_time, "time"); sm_io_fprintf(smioout, "\tcdb=%s\n", sm_cstr_data(aq_ta->aqt_cdb_id)); sm_io_fprintf(smioout, "\trcpts_tot=%u\n", aq_ta->aqt_rcpts_tot); sm_io_fprintf(smioout, "\trcpts_left=%u\n", aq_ta->aqt_rcpts_left); sm_io_fprintf(smioout, "\trcpts_temp=%u\n", aq_ta->aqt_rcpts_temp); sm_io_fprintf(smioout, "\trcpts_perm=%u\n", aq_ta->aqt_rcpts_perm); if (Verbose > 0) { sm_io_fprintf(smioout, "\trcpts_tried=%u\n", aq_ta->aqt_rcpts_tried); sm_io_fprintf(smioout, "\tnxt_idx=%u\n", aq_ta->aqt_nxt_idx); } sm_io_fprintf(smioout, "\tstate=%d\n", aq_ta->aqt_state); sm_io_fprintf(smioout, "\taqt_rcpts_ar=%d\n", aq_ta->aqt_rcpts_ar); sm_io_fprintf(smioout, "\taqt_owners_n=%d\n", aq_ta->aqt_owners_n); for (u = 0; u < aq_ta->aqt_owners_n; u++) { sm_io_fprintf(smioout, "\towner[%u]=%S\n", u, aq_ta->aqt_owners_pa[u]); } /* for checks... */ aq_ta->aqt_rcpts_inaq = aq_ta->aqt_rcpts_left; ok = lookup(tn, aq_ta->aqt_ss_ta_id, sizeof(aq_ta->aqt_ss_ta_id), TA_TYPE); if (!ok) goto error; ret = bht_add(t_aq, aq_ta->aqt_ss_ta_id, sizeof(aq_ta->aqt_ss_ta_id), aq_ta, &bi); SM_TEST(ret == SM_SUCCESS); SM_TEST(bi != NULL); } else if (rt == EDB_REQ_RCPT) { aq_rcpt_clr(aq_rcpt); ret = edb_rcpt_dec(edb_req, aq_rcpt); SM_TEST(ret == SM_SUCCESS); if (sm_is_err(ret)) goto error; aq_rcpt->aqr_ss_ta_id[SMTP_STID_SIZE - 1] = '\0'; if (IdPat && b_regmatch(&IdReg, aq_rcpt->aqr_ss_ta_id, 0) != 0) continue; if (AddrPat && b_regmatch(&AddrReg, (const char *) sm_str_getdata(aq_rcpt->aqr_pa), 0) != 0) continue; sm_io_fprintf(smioout, "got recipient\n"); sm_io_fprintf(smioout, "\tta_id=%s\n", aq_rcpt->aqr_ss_ta_id); sm_io_fprintf(smioout, "\trcpt=%S\n", aq_rcpt->aqr_pa); sm_io_fprintf(smioout, "\taqr_rcpt_idx=%u\n", aq_rcpt->aqr_idx); if (Verbose > 0) sm_io_fprintf(smioout, "\taqr_tries=%u\n", aq_rcpt->aqr_tries); if (Verbose > 0 && aq_rcpt->aqr_port != 0) sm_io_fprintf(smioout, "\taqr_port=%hd\n", aq_rcpt->aqr_port); if (aq_rcpt->aqr_addr_max > 0) { if (Verbose > 0) { for (u = 0; u < aq_rcpt->aqr_addr_max; ++u) { sm_io_fprintf(smioout, "\tsrv_ip4=%A\n", aq_rcpt->aqr_addrs[u].aqra_ipv4); printtime(aq_rcpt->aqr_addrs[u].aqra_expt, "expt"); sm_io_fprintf(smioout, "\tpref=%hu\n", aq_rcpt->aqr_addrs[u].aqra_pref); } } else { sm_io_fprintf(smioout, "\tsrv_ip4=%A\n", aq_rcpt->aqr_addrs[0].aqra_ipv4); } } else sm_io_fprintf(smioout, "\tsrv_ip4=undef\n"); sm_io_fprintf(smioout, "\taqr_da_idx=%u\n", aq_rcpt->aqr_da_idx); printtime(aq_rcpt->aqr_st_time, "aqr_st_time "); if (Verbose > 0) { printtime(aq_rcpt->aqr_last_try, "aqr_last_try"); printtime(aq_rcpt->aqr_next_try, "aqr_next_try"); } sm_io_fprintf(smioout, "\taqr_status=%d\n", aq_rcpt->aqr_status); if (Verbose > 0) { sm_io_fprintf(smioout, "\tflags=0x%x\n", aq_rcpt->aqr_flags); } if (Verbose > 0 && aq_rcpt->aqr_msg != NULL) { sm_io_fprintf(smioout, "\ttext=%S\n", aq_rcpt->aqr_msg); } if (Verbose > 0 && aq_rcpt->aqr_dsn_msg != NULL) { sm_io_fprintf(smioout, "\tdsn_msg=%S\n", aq_rcpt->aqr_dsn_msg); } if (Verbose > 0 && aq_rcpt->aqr_err_st != 0) { sm_io_fprintf(smioout, "\terr_st=0x%x\n", aq_rcpt->aqr_err_st); } if (Verbose > 0 && aq_rcpt->aqr_addr_fail != 0) { sm_io_fprintf(smioout, "\thost=0x%x\n", aq_rcpt->aqr_addr_fail); } if (Verbose > 0 && aq_rcpt_has_bounce(aq_rcpt)) { sm_io_fprintf(smioout, "\tbounce_idx=%u\n", aq_rcpt->aqr_dsn_idx); } if (Verbose > 0 && aq_rcpt_has_owner(aq_rcpt)) { sm_io_fprintf(smioout, "\towner_idx=%u\n", aq_rcpt->aqr_owner_idx); } #if MTA_USE_TLS if (Verbose > 1 && aq_rcpt->aqr_conf != NULL) { sm_io_fprintf(smioout, "\tconf=%S\n", aq_rcpt->aqr_conf); sm_io_fprintf(smioout, "\tmaprescnf=%#x\n", aq_rcpt->aqr_maprescnf); } #endif /* MTA_USE_TLS */ ok = lookup(tn, aq_rcpt->aqr_ss_ta_id, sizeof(aq_rcpt->aqr_ss_ta_id), RCPT_TYPE); if (!ok) goto error; aq_ta_f = bht_find(t_aq, aq_rcpt->aqr_ss_ta_id, sizeof(aq_rcpt->aqr_ss_ta_id)); /* this fails if the rcpt is found before the TA */ SM_TEST(aq_ta_f != NULL); if (aq_ta_f != NULL) { SM_TEST(aq_ta_f->aqt_rcpts_inaq > 0); --aq_ta_f->aqt_rcpts_inaq; } } else if (rt != EDB_REQ_VRS) { sm_io_fprintf(smioout, "got what? %x\n", rt); SM_TEST(false); if (sm_is_err(ret)) goto error; } } bht_walk(t_aq, walk_ta, NULL); bht_walk(tn, walk_fn, NULL); error: SM_FREE(aq_rcpt); if (aq_ta != NULL) { SM_STR_FREE(aq_ta->aqt_mail->aqm_pa); SM_FREE(aq_ta->aqt_mail); SM_CSTR_FREE(aq_ta->aqt_cdb_id); sm_free(aq_ta); } sm_io_flush(smioout); if (edb_ctx != NULL && edb_cursor != NULL) { ret = edb_rd_close(edb_ctx, edb_cursor); SM_TEST(ret == SM_SUCCESS); } if (aq_ctx != NULL) { ret = aq_close(aq_ctx); SM_TEST(ret == SM_SUCCESS); } if (edb_ctx != NULL) { ret = edb_close(edb_ctx); SM_TEST(ret == SM_SUCCESS); } if (tn != NULL) bht_destroy(tn, free_fn, NULL); if (t_aq != NULL) bht_destroy(t_aq, free_aqs, NULL); return; } static void usage(const char *prg) { fprintf(stderr, "usage: %s [options]\n" "Print content of main mail queue and" " test it for consistency.\n" "options:\n" "-I regex: print only entries whose ID match regex\n" "-R regex: print only entries whose address match regex\n" "-V: increase verbosity (can be used multiple times)\n" "-w: open DB environment read/write\n" , prg); exit(0); } static int b_regcomp(regex_t *preg, const char *pattern, int pflags) { int regerr; regerr = regcomp(preg, pattern, pflags); if (regerr != 0) { /* Errorhandling */ char errbuf[ERRBUF_SIZE]; (void) regerror(regerr, preg, errbuf, sizeof errbuf); fprintf(stderr, "pattern-compile-error: %s", errbuf); } return regerr; } int main(int argc, char *argv[]) { int r, c; char *prg; int regopts; uint flags; regopts = REG_EXTENDED; opterr = 0; Verbose = 0; prg = argv[0]; IdPat = false; AddrPat = false; flags = EDB_OPEN_RDONLY|EDB_OPEN_NOENV; while ((r = getopt(argc, argv, "I:R:rVvw")) != -1) { switch (r) { case 'I': c = b_regcomp(&IdReg, optarg, regopts); if (c != 0) { fprintf(stderr, "regcomp(%s) failed=%d\n", optarg, c); return 1; } IdPat = true; break; case 'R': c = b_regcomp(&AddrReg, optarg, regopts); if (c != 0) { fprintf(stderr, "regcomp(%s) failed=%d\n", optarg, c); return 1; } AddrPat = true; break; case 'r': flags = EDB_OPEN_RDONLY; break; case 'v': flags |= EDB_OPEN_NOVERSION; break; case 'V': ++Verbose; break; case 'w': flags = EDB_OPEN_RDWR; break; default: usage(prg); /* NOTREACHED */ break; } } sm_test_begin(argc, argv, "test edbr 1"); argc -= optind; argv += optind; edbr1(flags); return sm_test_end(); }