/* * Copyright (c) 2002-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-0.c,v 1.65 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/edbcnf.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); } /* 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; } 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 edbr0(edb_cnf_P edb_cnf, uint flags) { sm_ret_T ret; sm_ret_T rt; edb_ctx_P edb_ctx; aq_ctx_P aq_ctx; aq_ctx_T aq_ctx_s; aq_rcpt_P aq_rcpt; aq_ta_P aq_ta; edb_req_P edb_req; edb_cursor_P edb_cursor; bht_P tn; bool ok; aq_rcpt = NULL; aq_ta = NULL; edb_req = NULL; tn = NULL; edb_cursor = NULL; edb_ctx = NULL; aq_ctx = &aq_ctx_s; edb_cnf->edbcnf_oflags = flags; ret = edb_open(edb_cnf, NULL, NULL, &edb_ctx); SM_TEST(ret == SM_SUCCESS); if (sm_is_err(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; aq_ta = new_ta(); SM_TEST(aq_ta != NULL); if (aq_ta == 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; 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) { 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); } if (Verbose > 5) { sm_io_fprintf(smioout, "\tsize=%lu B\n", (ulong) aq_ta->aqt_msg_sz_b); } 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); ok = lookup(tn, aq_ta->aqt_ss_ta_id, sizeof(aq_ta->aqt_ss_ta_id), TA_TYPE); if (!ok) goto error; } 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 (aq_rcpt->aqr_addr_max > 0) sm_io_fprintf(smioout, "\tsrv_ip4=0x%x\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 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 ok = lookup(tn, aq_rcpt->aqr_ss_ta_id, sizeof(aq_rcpt->aqr_ss_ta_id), RCPT_TYPE); if (!ok) goto error; } 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(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); 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" "-r open DEFEDB with recovery\n" "-V increase verbosity\n" "-v do not check DEFEDB version\n" "-w open DEFEDB R/W\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; edb_cnf_T edb_cnf; regopts = REG_EXTENDED; opterr = 0; Verbose = 0; prg = argv[0]; IdPat = false; AddrPat = false; flags = EDB_OPEN_RDONLY|EDB_OPEN_NOENV; sm_memzero(&edb_cnf, sizeof(edb_cnf)); edb_cnf.edbcnf_basedir = EDB_HOME; while ((r = getopt(argc, argv, "b:I:R:rVvw")) != -1) { switch (r) { case 'b': break; 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_RDWR|EDB_OPEN_RECOVER; break; case 'V': ++Verbose; break; case 'v': flags |= EDB_OPEN_NOVERSION; break; case 'w': flags = EDB_OPEN_RDWR; break; default: usage(prg); } } sm_test_begin(argc, argv, "test edbr 0"); argc -= optind; argv += optind; r = mkdir(edb_cnf.edbcnf_basedir, 0700); edbr0(&edb_cnf, flags); return sm_test_end(); }