char *safe_ultostr(VSTRING *buf, unsigned long ulval, int base, int padlen, int padchar) { const char *myname = "safe_ultostr"; char *start; char *last; int i; /* * Sanity check. */ if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) msg_panic("%s: bad base: %d", myname, base); /* * First accumulate the result, backwards. */ VSTRING_RESET(buf); while (ulval != 0) { VSTRING_ADDCH(buf, safe_chars[ulval % base]); ulval /= base; } while (VSTRING_LEN(buf) < padlen) VSTRING_ADDCH(buf, padchar); VSTRING_TERMINATE(buf); /* * Then, reverse the result. */ start = STR(buf); last = END(buf) - 1; for (i = 0; i < VSTRING_LEN(buf) / 2; i++) SWAP(int, start[i], last[-i]); return (STR(buf)); }
const char *cleanup_strflags(unsigned flags) { static VSTRING *result; unsigned i; if (flags == 0) return ("none"); if (result == 0) result = vstring_alloc(20); else VSTRING_RESET(result); for (i = 0; i < sizeof(cleanup_flag_map) / sizeof(cleanup_flag_map[0]); i++) { if (cleanup_flag_map[i].flag & flags) { vstring_sprintf_append(result, "%s ", cleanup_flag_map[i].text); flags &= ~cleanup_flag_map[i].flag; } } if (flags != 0 || VSTRING_LEN(result) == 0) msg_panic("cleanup_strflags: unrecognized flag value(s) 0x%x", flags); vstring_truncate(result, VSTRING_LEN(result) - 1); VSTRING_TERMINATE(result); return (vstring_str(result)); }
static const char *dict_union_lookup(DICT *dict, const char *query) { static const char myname[] = "dict_union_lookup"; DICT_UNION *dict_union = (DICT_UNION *) dict; DICT *map; char **cpp; char *dict_type_name; const char *result = 0; /* * After Roel van Meer, postfix-users mailing list, Sept 2014. */ VSTRING_RESET(dict_union->re_buf); for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) { if ((map = dict_handle(dict_type_name)) == 0) msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name); if ((result = dict_get(map, query)) == 0) continue; if (VSTRING_LEN(dict_union->re_buf) > 0) VSTRING_ADDCH(dict_union->re_buf, ','); vstring_strcat(dict_union->re_buf, result); } DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, VSTRING_LEN(dict_union->re_buf) > 0 ? STR(dict_union->re_buf) : 0); }
static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result) { DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict; int len = strlen(name); int buflen; /* * We won't get integer overflows in 2*len + 1, because Postfix input * keys have reasonable size limits, better safe than sorry. */ if (len > (INT_MAX - VSTRING_LEN(result) - 1) / 2) msg_panic("dict_mysql_quote: integer overflow in %lu+2*%d+1", (unsigned long) VSTRING_LEN(result), len); buflen = 2 * len + 1; VSTRING_SPACE(result, buflen); #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 if (dict_mysql->active_host) mysql_real_escape_string(dict_mysql->active_host->db, vstring_end(result), name, len); else #endif mysql_escape_string(vstring_end(result), name, len); VSTRING_SKIP(result); }
int main(int unused_argc, char **unused_argv) { VSTRING *buf = vstring_alloc(100); VSTRING *result = vstring_alloc(100); char *cp; char *name; char *value; HTABLE *table; int stat; while (!vstream_feof(VSTREAM_IN)) { table = htable_create(0); /* * Read a block of definitions, terminated with an empty line. */ while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { vstream_printf("<< %s\n", vstring_str(buf)); vstream_fflush(VSTREAM_OUT); if (VSTRING_LEN(buf) == 0) break; cp = vstring_str(buf); name = mystrtok(&cp, " \t\r\n="); value = mystrtok(&cp, " \t\r\n="); htable_enter(table, name, value ? mystrdup(value) : 0); } /* * Read a block of patterns, terminated with an empty line or EOF. */ while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { vstream_printf("<< %s\n", vstring_str(buf)); vstream_fflush(VSTREAM_OUT); if (VSTRING_LEN(buf) == 0) break; cp = vstring_str(buf); VSTRING_RESET(result); stat = mac_expand(result, vstring_str(buf), MAC_EXP_FLAG_NONE, (char *) 0, lookup, (char *) table); vstream_printf("stat=%d result=%s\n", stat, vstring_str(result)); vstream_fflush(VSTREAM_OUT); } htable_free(table, myfree); vstream_printf("\n"); } /* * Clean up. */ vstring_free(buf); vstring_free(result); exit(0); }
VSTRING *vstring_truncate(VSTRING *vp, ssize_t len) { ssize_t move; if (len < 0) { len = (-len); if ((move = VSTRING_LEN(vp) - len) > 0) memmove(vstring_str(vp), vstring_str(vp) + move, len); } if (len < VSTRING_LEN(vp)) VSTRING_AT_OFFSET(vp, len); return (vp); }
int main(int unused_argc, char **unused_argv) { VSTRING *extension = vstring_alloc(1); VSTRING *buf = vstring_alloc(1); ARGV *argv; char **cpp; mail_conf_read(); if (chdir(var_queue_dir) < 0) msg_fatal("chdir %s: %m", var_queue_dir); vstream_printf("extension: (CR for none): "); vstream_fflush(VSTREAM_OUT); if (vstring_get_nonl(extension, VSTREAM_IN) == VSTREAM_EOF) exit(0); vstream_printf("print strings to be translated, one per line\n"); vstream_fflush(VSTREAM_OUT); while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { argv = mail_addr_crunch(STR(buf), VSTRING_LEN(extension) ? STR(extension) : 0); for (cpp = argv->argv; *cpp; cpp++) vstream_printf(" %s\n", *cpp); vstream_fflush(VSTREAM_OUT); } return (0); }
VSTRING *off_cvt_number(VSTRING *buf, off_t offset) { static char digs[] = "0123456789"; char *start; char *last; int i; /* * Sanity checks */ if (offset < 0) msg_panic("off_cvt_number: negative offset -%s", STR(off_cvt_number(buf, -offset))); /* * First accumulate the result, backwards. */ VSTRING_RESET(buf); while (offset != 0) { VSTRING_ADDCH(buf, digs[offset % 10]); offset /= 10; } VSTRING_TERMINATE(buf); /* * Then, reverse the result. */ start = STR(buf); last = END(buf) - 1; for (i = 0; i < VSTRING_LEN(buf) / 2; i++) SWAP(int, start[i], last[-i]); return (buf); }
static char *xsasl_dovecot_server_mech_filter(ARGV *mechanism_argv, XSASL_DCSRV_MECH *mechanism_list, unsigned int conf_props) { const char *myname = "xsasl_dovecot_server_mech_filter"; unsigned int pos_conf_props = (conf_props & SEC_PROPS_POS_MASK); unsigned int neg_conf_props = (conf_props & SEC_PROPS_NEG_MASK); VSTRING *mechanisms_str = vstring_alloc(10); XSASL_DCSRV_MECH *mp; /* * Match Postfix properties against Dovecot server properties. */ for (mp = mechanism_list; mp != 0; mp = mp->next) { if ((mp->sec_props & pos_conf_props) == pos_conf_props && (mp->sec_props & neg_conf_props) == 0) { if (VSTRING_LEN(mechanisms_str) > 0) VSTRING_ADDCH(mechanisms_str, ' '); vstring_strcat(mechanisms_str, mp->mech_name); argv_add(mechanism_argv, mp->mech_name, (char *) 0); if (msg_verbose) msg_info("%s: keep mechanism: %s", myname, mp->mech_name); } else { if (msg_verbose) msg_info("%s: skip mechanism: %s", myname, mp->mech_name); } } return (vstring_export(mechanisms_str)); }
VSTRING *vstring_memcat(VSTRING *vp, const char *src, ssize_t len) { VSTRING_SPACE(vp, len); memcpy(vstring_end(vp), src, len); len += VSTRING_LEN(vp); VSTRING_AT_OFFSET(vp, len); return (vp); }
int memcache_get(VSTREAM *stream, VSTRING *vp, ssize_t bound) { int last_char; int next_char; last_char = (bound == 0 ? vstring_get(vp, stream) : vstring_get_bound(vp, stream, bound)); switch (last_char) { /* * Do some repair in the rare case that we stopped reading in the * middle of the CRLF record terminator. */ case '\r': if ((next_char = VSTREAM_GETC(stream)) == '\n') { VSTRING_ADDCH(vp, '\n'); /* FALLTRHOUGH */ } else { if (next_char != VSTREAM_EOF) vstream_ungetc(stream, next_char); /* * Input too long, or EOF */ default: if (msg_verbose) msg_info("%s got %s", VSTREAM_PATH(stream), LEN(vp) < bound ? "EOF" : "input too long"); return (-1); } /* * Strip off the record terminator: either CRLF or just bare LF. */ case '\n': vstring_truncate(vp, VSTRING_LEN(vp) - 1); if (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r') vstring_truncate(vp, VSTRING_LEN(vp) - 1); VSTRING_TERMINATE(vp); if (msg_verbose) msg_info("%s got: %s", VSTREAM_PATH(stream), STR(vp)); return (0); } }
int psc_send_reply(PSC_STATE *state, const char *text) { ssize_t start; int ret; const char *footer; ssize_t text_len = strlen(text) - 2; if (msg_verbose) msg_info("> [%s]:%s: %.*s", state->smtp_client_addr, state->smtp_client_port, (int) text_len, text); /* * Append the new text to earlier text that could not be sent because the * output was throttled. */ start = VSTRING_LEN(state->send_buf); vstring_strcat(state->send_buf, text); /* * For soft_bounce support, we also fix the REJECT logging before the * dummy SMTP engine calls the psc_send_reply() output routine. We do * some double work, but it is for debugging only. */ if (var_soft_bounce) { if (text[0] == '5') STR(state->send_buf)[start + 0] = '4'; if (text[4] == '5') STR(state->send_buf)[start + 4] = '4'; } /* * Append the optional reply footer. */ if ((*text == '4' || *text == '5') && ((psc_rej_ftr_maps != 0 && (footer = psc_get_footer(text, text_len)) != 0) || *(footer = var_psc_rej_footer) != 0)) smtp_reply_footer(state->send_buf, start, footer, STR(psc_expand_filter), psc_expand_lookup, (void *) state); /* * Do a best effort sending text, but don't block when the output is * throttled by a hostile peer. */ ret = write(vstream_fileno(state->smtp_client_stream), STR(state->send_buf), LEN(state->send_buf)); if (ret > 0) vstring_truncate(state->send_buf, ret - LEN(state->send_buf)); if (ret < 0 && errno != EAGAIN && errno != EPIPE && errno != ECONNRESET) msg_warn("write [%s]:%s: %m", state->smtp_client_addr, state->smtp_client_port); return (ret < 0 && errno != EAGAIN); }
static int smtpd_proxy_xforward_flush(SMTPD_STATE *state, VSTRING *buf) { int ret; if (VSTRING_LEN(buf) > 0) { ret = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, XFORWARD_CMD "%s", STR(buf)); VSTRING_RESET(buf); return (ret); } return (0); }
VSTRING *vstring_prepend(VSTRING *vp, const char *buf, ssize_t len) { ssize_t new_len; /* * Sanity check. */ if (len < 0) msg_panic("vstring_prepend: bad length %ld", (long) len); /* * Move the existing content and copy the new content. */ new_len = VSTRING_LEN(vp) + len; VSTRING_SPACE(vp, len); memmove(vstring_str(vp) + len, vstring_str(vp), VSTRING_LEN(vp)); memcpy(vstring_str(vp), buf, len); VSTRING_AT_OFFSET(vp, new_len); VSTRING_TERMINATE(vp); return (vp); }
int main(int unused_argc, char **unused_argv) { VSTRING *in = vstring_alloc(10); VSTRING *out = vstring_alloc(10); while (vstring_fgets_nonl(in, VSTREAM_IN)) { unescape(out, vstring_str(in)); vstream_fwrite(VSTREAM_OUT, vstring_str(out), VSTRING_LEN(out)); } vstream_fflush(VSTREAM_OUT); exit(0); }
/* * Locate or allocate connection cache entry. */ static void dict_ldap_conn_find(DICT_LDAP *dict_ldap) { VSTRING *keybuf = vstring_alloc(10); char *key; int len; #ifdef LDAP_API_FEATURE_X_OPENLDAP int sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl; #endif LDAP_CONN *conn; #define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1) #define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu", (unsigned long)(i)) ADDSTR(keybuf, dict_ldap->server_host); ADDINT(keybuf, dict_ldap->server_port); ADDINT(keybuf, dict_ldap->bind); ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_dn : ""); ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_pw : ""); ADDINT(keybuf, dict_ldap->dereference); ADDINT(keybuf, dict_ldap->chase_referrals); ADDINT(keybuf, dict_ldap->debuglevel); ADDINT(keybuf, dict_ldap->version); #ifdef LDAP_API_FEATURE_X_OPENLDAP ADDINT(keybuf, dict_ldap->ldap_ssl); ADDINT(keybuf, dict_ldap->start_tls); ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0); ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_file : ""); ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_dir : ""); ADDSTR(keybuf, sslon ? dict_ldap->tls_cert : ""); ADDSTR(keybuf, sslon ? dict_ldap->tls_key : ""); ADDSTR(keybuf, sslon ? dict_ldap->tls_random_file : ""); ADDSTR(keybuf, sslon ? dict_ldap->tls_cipher_suite : ""); #endif key = vstring_str(keybuf); len = VSTRING_LEN(keybuf); if (conn_hash == 0) conn_hash = binhash_create(0); if ((dict_ldap->ht = binhash_locate(conn_hash, key, len)) == 0) { conn = (LDAP_CONN *) mymalloc(sizeof(LDAP_CONN)); conn->conn_ld = 0; conn->conn_refcount = 0; dict_ldap->ht = binhash_enter(conn_hash, key, len, (char *) conn); } ++DICT_LDAP_CONN(dict_ldap)->conn_refcount; vstring_free(keybuf); }
static int smtpd_proxy_xforward_send(SMTPD_STATE *state, VSTRING *buf, const char *name, int value_available, const char *value) { size_t new_len; int ret; #define CONSTR_LEN(s) (sizeof(s) - 1) #define PAYLOAD_LIMIT (512 - CONSTR_LEN("250 " XFORWARD_CMD "\r\n")) if (!value_available) value = XFORWARD_UNAVAILABLE; /* * Encode the attribute value. */ if (state->expand_buf == 0) state->expand_buf = vstring_alloc(100); xtext_quote(state->expand_buf, value, ""); /* * How much space does this attribute need? SPACE name = value. */ new_len = strlen(name) + strlen(STR(state->expand_buf)) + 2; if (new_len > PAYLOAD_LIMIT) msg_warn("%s command payload %s=%.10s... exceeds SMTP protocol limit", XFORWARD_CMD, name, value); /* * Flush the buffer if we need to, and store the attribute. */ if (VSTRING_LEN(buf) > 0 && VSTRING_LEN(buf) + new_len > PAYLOAD_LIMIT) if ((ret = smtpd_proxy_xforward_flush(state, buf)) < 0) return (ret); vstring_sprintf_append(buf, " %s=%s", name, STR(state->expand_buf)); return (0); }
VSTRING *vstring_sprintf_prepend(VSTRING *vp, const char *format,...) { va_list ap; ssize_t old_len = VSTRING_LEN(vp); ssize_t result_len; /* Construct: old|new|free */ va_start(ap, format); vp = vstring_vsprintf_append(vp, format, ap); va_end(ap); result_len = VSTRING_LEN(vp); /* Construct: old|new|old|free */ VSTRING_SPACE(vp, old_len); vstring_memcat(vp, vstring_str(vp), old_len); /* Construct: new|old|free */ memmove(vstring_str(vp), vstring_str(vp) + old_len, result_len); VSTRING_AT_OFFSET(vp, result_len); VSTRING_TERMINATE(vp); return (vp); }
const char *str_long_name_mask_opt(VSTRING *buf, const char *context, const LONG_NAME_MASK * table, long mask, int flags) { const char *myname = "name_mask"; int len; static VSTRING *my_buf = 0; int delim = (flags & NAME_MASK_COMMA ? ',' : (flags & NAME_MASK_PIPE ? '|' : ' ')); const LONG_NAME_MASK *np; if ((flags & STR_NAME_MASK_REQUIRED) == 0) msg_panic("%s: missing NAME_MASK_NUMBER/FATAL/RETURN/WARN/IGNORE flag", myname); if (buf == 0) { if (my_buf == 0) my_buf = vstring_alloc(1); buf = my_buf; } VSTRING_RESET(buf); for (np = table; mask != 0; np++) { if (np->name == 0) { if (flags & NAME_MASK_NUMBER) { vstring_sprintf_append(buf, "0x%lx%c", mask, delim); } else if (flags & NAME_MASK_FATAL) { msg_fatal("%s: unknown %s bit in mask: 0x%lx", myname, context, mask); } else if (flags & NAME_MASK_RETURN) { msg_warn("%s: unknown %s bit in mask: 0x%lx", myname, context, mask); return (0); } else if (flags & NAME_MASK_WARN) { msg_warn("%s: unknown %s bit in mask: 0x%lx", myname, context, mask); } break; } if (mask & np->mask) { mask &= ~np->mask; vstring_sprintf_append(buf, "%s%c", np->name, delim); } } if ((len = VSTRING_LEN(buf)) > 0) vstring_truncate(buf, len - 1); VSTRING_TERMINATE(buf); return (STR(buf)); }
int main(int argc, char **argv) { VSTRING *in = vstring_alloc(10); VSTRING *out = vstring_alloc(10); int un_escape = 1; if (argc > 2 || (argc > 1 && (un_escape = strcmp(argv[1], "-e"))) != 0) msg_fatal("usage: %s [-e (escape)]", argv[0]); if (un_escape) { while (vstring_fgets_nonl(in, VSTREAM_IN)) { unescape(out, vstring_str(in)); vstream_fwrite(VSTREAM_OUT, vstring_str(out), VSTRING_LEN(out)); } } else { while (vstring_fgets(in, VSTREAM_IN)) { escape(out, vstring_str(in), VSTRING_LEN(in)); vstream_fwrite(VSTREAM_OUT, vstring_str(out), VSTRING_LEN(out)); } } vstream_fflush(VSTREAM_OUT); exit(0); }
int main(int unused_argc, char **unused_argv) { VSTRING *vp = vstring_alloc(100); TOK822 *list; VSTRING *buf = vstring_alloc(100); #define TEST_TOKEN_LIMIT 20 while (readlline(buf, VSTREAM_IN, (int *) 0)) { while (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\n') { vstring_end(buf)[-1] = 0; vstring_truncate(buf, VSTRING_LEN(buf) - 1); } if (!isatty(vstream_fileno(VSTREAM_IN))) vstream_printf(">>>%s<<<\n\n", vstring_str(buf)); list = tok822_parse_limit(vstring_str(buf), TEST_TOKEN_LIMIT); vstream_printf("Parse tree:\n"); tok822_print(list, 0); vstream_printf("\n"); vstream_printf("Internalized:\n%s\n\n", vstring_str(tok822_internalize(vp, list, TOK822_STR_DEFL))); vstream_fflush(VSTREAM_OUT); vstream_printf("Externalized, no newlines inserted:\n%s\n\n", vstring_str(tok822_externalize(vp, list, TOK822_STR_DEFL | TOK822_STR_TRNC))); vstream_fflush(VSTREAM_OUT); vstream_printf("Externalized, newlines inserted:\n%s\n\n", vstring_str(tok822_externalize(vp, list, TOK822_STR_DEFL | TOK822_STR_LINE | TOK822_STR_TRNC))); vstream_fflush(VSTREAM_OUT); tok822_free_tree(list); } vstring_free(vp); vstring_free(buf); return (0); }
VSTRING *vstring_insert(VSTRING *vp, ssize_t start, const char *buf, ssize_t len) { ssize_t new_len; /* * Sanity check. */ if (start < 0 || start >= VSTRING_LEN(vp)) msg_panic("vstring_insert: bad start %ld", (long) start); if (len < 0) msg_panic("vstring_insert: bad length %ld", (long) len); /* * Move the existing content and copy the new content. */ new_len = VSTRING_LEN(vp) + len; VSTRING_SPACE(vp, len); memmove(vstring_str(vp) + start + len, vstring_str(vp) + start, VSTRING_LEN(vp) - start); memcpy(vstring_str(vp) + start, buf, len); VSTRING_AT_OFFSET(vp, new_len); VSTRING_TERMINATE(vp); return (vp); }
int tls_mgr_lookup(const char *unused_type, const char *key, VSTRING *buf) { VSTRING *s; if (tls_cache == 0) return TLS_MGR_STAT_ERR; if ((s = (VSTRING *) htable_find(tls_cache, key)) == 0) return TLS_MGR_STAT_ERR; vstring_memcpy(buf, vstring_str(s), VSTRING_LEN(s)); ++cache_hits; return (TLS_MGR_STAT_OK); }
static char *pop3_quoted(char *cp, POP3D_TOKEN *arg, int start, int last) { static VSTRING *stack; int wanted; int c; /* * Parser stack. `ch' is always the most-recently entered character. */ #define ENTER_CHAR(buf, ch) VSTRING_ADDCH(buf, ch); #define LEAVE_CHAR(buf, ch) { \ vstring_truncate(buf, VSTRING_LEN(buf) - 1); \ ch = vstring_end(buf)[-1]; \ } if (stack == 0) stack = vstring_alloc(1); VSTRING_RESET(stack); ENTER_CHAR(stack, wanted = last); VSTRING_ADDCH(arg->vstrval, start); for (;;) { if ((c = *cp) == 0) break; cp++; VSTRING_ADDCH(arg->vstrval, c); if (c == '\\') { /* parse escape sequence */ if ((c = *cp) == 0) break; cp++; VSTRING_ADDCH(arg->vstrval, c); } else if (c == wanted) { /* closing quote etc. */ if (VSTRING_LEN(stack) == 1) return (cp); LEAVE_CHAR(stack, wanted); } else if (c == '"') { ENTER_CHAR(stack, wanted = '"'); /* highest precedence */ } else if (c == '<' && wanted == '>') { ENTER_CHAR(stack, wanted = '>'); /* lowest precedence */ } } arg->tokval = POP3D_TOK_ERROR; /* missing end */ return (cp); }
int main(int argc, char **argv) { SESSION *session; char *host; char *port; char *path; int path_len; int sessions = 1; int ch; int i; char *buf; const char *parse_err; struct addrinfo *res; int aierr; const char *protocols = INET_PROTO_NAME_ALL; INET_PROTO_INFO *proto_info; char *message_file = 0; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; signal(SIGPIPE, SIG_IGN); msg_vstream_init(argv[0], VSTREAM_ERR); /* * Parse JCL. */ while ((ch = GETOPT(argc, argv, "46AcC:df:F:l:Lm:M:Nor:R:s:S:t:T:vw:")) > 0) { switch (ch) { case '4': protocols = INET_PROTO_NAME_IPV4; break; case '6': protocols = INET_PROTO_NAME_IPV6; break; case 'A': allow_reject = 1; break; case 'c': count++; break; case 'C': if ((connect_count = atoi(optarg)) <= 0) msg_fatal("bad connection count: %s", optarg); break; case 'd': disconnect = 0; break; case 'f': sender = optarg; break; case 'F': if (message_file == 0 && message_length > 0) msg_fatal("-l option cannot be used with -F"); message_file = optarg; break; case 'l': if (message_file != 0) msg_fatal("-l option cannot be used with -F"); if ((message_length = atoi(optarg)) <= 0) msg_fatal("bad message length: %s", optarg); break; case 'L': talk_lmtp = 1; break; case 'm': if ((message_count = atoi(optarg)) <= 0) msg_fatal("bad message count: %s", optarg); break; case 'M': if (*optarg == '[') { if (!valid_mailhost_literal(optarg, DO_GRIPE)) msg_fatal("bad address literal: %s", optarg); } else { if (!valid_hostname(optarg, DO_GRIPE)) msg_fatal("bad hostname: %s", optarg); } var_myhostname = optarg; break; case 'N': number_rcpts = 1; break; case 'o': send_helo_first = 0; send_headers = 0; break; case 'r': if ((recipients = atoi(optarg)) <= 0) msg_fatal("bad recipient count: %s", optarg); break; case 'R': if (fixed_delay > 0) msg_fatal("do not use -w and -R options at the same time"); if ((random_delay = atoi(optarg)) <= 0) msg_fatal("bad random delay: %s", optarg); break; case 's': if ((sessions = atoi(optarg)) <= 0) msg_fatal("bad session count: %s", optarg); break; case 'S': subject = optarg; break; case 't': recipient = optarg; break; case 'T': if ((inet_windowsize = atoi(optarg)) <= 0) msg_fatal("bad TCP window size: %s", optarg); break; case 'v': msg_verbose++; break; case 'w': if (random_delay > 0) msg_fatal("do not use -w and -R options at the same time"); if ((fixed_delay = atoi(optarg)) <= 0) msg_fatal("bad fixed delay: %s", optarg); break; default: usage(argv[0]); } } if (argc - optind != 1) usage(argv[0]); if (random_delay > 0) srand(getpid()); /* * Initialize the message content, SMTP encoded. smtp_fputs() will append * another \r\n but we don't care. */ if (message_file != 0) { VSTREAM *fp; VSTRING *buf = vstring_alloc(100); VSTRING *msg = vstring_alloc(100); if ((fp = vstream_fopen(message_file, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", message_file); while (vstring_get_nonl(buf, fp) != VSTREAM_EOF) { if (*vstring_str(buf) == '.') VSTRING_ADDCH(msg, '.'); vstring_memcat(msg, vstring_str(buf), VSTRING_LEN(buf)); vstring_memcat(msg, "\r\n", 2); } if (vstream_ferror(fp)) msg_fatal("read %s: %m", message_file); vstream_fclose(fp); vstring_free(buf); message_length = VSTRING_LEN(msg); message_data = vstring_export(msg); send_headers = 0; } else if (message_length > 0) { message_data = mymalloc(message_length); memset(message_data, 'X', message_length); for (i = 80; i < message_length; i += 80) { message_data[i - 80] = "0123456789"[(i / 80) % 10]; message_data[i - 2] = '\r'; message_data[i - 1] = '\n'; } } /* * Translate endpoint address to internal form. */ proto_info = inet_proto_init("protocols", protocols); if (strncmp(argv[optind], "unix:", 5) == 0) { path = argv[optind] + 5; path_len = strlen(path); if (path_len >= (int) sizeof(sun.sun_path)) msg_fatal("unix-domain name too long: %s", path); memset((char *) &sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; #ifdef HAS_SUN_LEN sun.sun_len = path_len + 1; #endif memcpy(sun.sun_path, path, path_len); sa = (struct sockaddr *) & sun; sa_length = sizeof(sun); } else { if (strncmp(argv[optind], "inet:", 5) == 0) argv[optind] += 5; buf = mystrdup(argv[optind]); if ((parse_err = host_port(buf, &host, (char *) 0, &port, "smtp")) != 0) msg_fatal("%s: %s", argv[optind], parse_err); if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0) msg_fatal("%s: %s", argv[optind], MAI_STRERROR(aierr)); myfree(buf); sa = (struct sockaddr *) & ss; if (res->ai_addrlen > sizeof(ss)) msg_fatal("address length %d > buffer length %d", (int) res->ai_addrlen, (int) sizeof(ss)); memcpy((char *) sa, res->ai_addr, res->ai_addrlen); sa_length = res->ai_addrlen; #ifdef HAS_SA_LEN sa->sa_len = sa_length; #endif freeaddrinfo(res); } /* * Make sure the SMTP server cannot run us out of memory by sending * never-ending lines of text. */ if (buffer == 0) { buffer = vstring_alloc(100); vstring_ctl(buffer, VSTRING_CTL_MAXLEN, (ssize_t) var_line_limit, 0); } /* * Make sure we have sender and recipient addresses. */ if (var_myhostname == 0) var_myhostname = get_hostname(); if (sender == 0 || recipient == 0) { vstring_sprintf(buffer, "foo@%s", var_myhostname); defaddr = mystrdup(vstring_str(buffer)); if (sender == 0) sender = defaddr; if (recipient == 0) recipient = defaddr; } /* * Start sessions. */ while (sessions-- > 0) { session = (SESSION *) mymalloc(sizeof(*session)); session->stream = 0; session->xfer_count = 0; session->connect_count = connect_count; session->next = 0; session_count++; startup(session); } for (;;) { event_loop(-1); if (session_count <= 0 && message_count <= 0) { if (count) { VSTREAM_PUTC('\n', VSTREAM_OUT); vstream_fflush(VSTREAM_OUT); } exit(0); } } }
static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) { VSTRING *buf = vstring_alloc(100); CLEANUP_STATE *state; int flags; int type = 0; int status; /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * Open a queue file and initialize state. */ state = cleanup_open(src); /* * Send the queue id to the client. Read client processing options. If we * can't read the client processing options we can pretty much forget * about the whole operation. */ attr_print(src, ATTR_FLAG_NONE, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, state->queue_id, ATTR_TYPE_END); if (attr_scan(src, ATTR_FLAG_STRICT, ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &flags, ATTR_TYPE_END) != 1) { state->errs |= CLEANUP_STAT_BAD; flags = 0; } cleanup_control(state, flags); /* * XXX Rely on the front-end programs to enforce record size limits. * * First, copy the envelope records to the queue file. Then, copy the * message content (headers and body). Finally, attach any information * extracted from message headers. */ while (CLEANUP_OUT_OK(state)) { if ((type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) < 0) { state->errs |= CLEANUP_STAT_BAD; break; } if (REC_GET_HIDDEN_TYPE(type)) { msg_warn("%s: record type %d not allowed - discarding this message", state->queue_id, type); state->errs |= CLEANUP_STAT_BAD; break; } CLEANUP_RECORD(state, type, vstring_str(buf), VSTRING_LEN(buf)); if (type == REC_TYPE_END) break; } /* * Keep reading in case of problems, until the sender is ready to receive * our status report. */ if (CLEANUP_OUT_OK(state) == 0 && type > 0) { while (type != REC_TYPE_END && (type = rec_get(src, buf, 0)) > 0) /* void */ ; } /* * Log something to make timeout errors easier to debug. */ if (vstream_ftimeout(src)) msg_warn("%s: read timeout on %s", state->queue_id, VSTREAM_PATH(src)); /* * Finish this message, and report the result status to the client. */ status = cleanup_flush(state); /* in case state is modified */ attr_print(src, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_STR, MAIL_ATTR_WHY, (state->flags & CLEANUP_FLAG_SMTP_REPLY) && state->smtp_reply ? state->smtp_reply : state->reason ? state->reason : "", ATTR_TYPE_END); cleanup_free(state); /* * Cleanup. */ vstring_free(buf); }
static int mac_expand_callback(int type, VSTRING *buf, char *ptr) { char *myname = "mac_expand_callback"; MAC_EXP *mc = (MAC_EXP *) ptr; int lookup_mode; const char *text; char *cp; int ch; int len; /* * Sanity check. */ if (mc->level++ > 100) { msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf)); mc->status |= MAC_PARSE_ERROR; } if (mc->status & MAC_PARSE_ERROR) return (mc->status); /* * $Name etc. reference. */ if (type == MAC_PARSE_VARNAME) { /* * Look for the ? or : delimiter. In case of a syntax error, return * without doing damage, and issue a warning instead. */ for (cp = vstring_str(buf); /* void */ ; cp++) { if ((ch = *cp) == 0) { lookup_mode = MAC_EXP_MODE_USE; break; } if (ch == '?' || ch == ':') { *cp++ = 0; lookup_mode = MAC_EXP_MODE_TEST; break; } if (!ISALNUM(ch) && ch != '_') { msg_warn("macro name syntax error: \"%s\"", vstring_str(buf)); mc->status |= MAC_PARSE_ERROR; return (mc->status); } } /* * Look up the named parameter. */ text = mc->lookup(vstring_str(buf), lookup_mode, mc->context); /* * Perform the requested substitution. */ switch (ch) { case '?': if (text != 0 && *text != 0) mac_parse(cp, mac_expand_callback, (char *) mc); break; case ':': if (text == 0 || *text == 0) mac_parse(cp, mac_expand_callback, (char *) mc); break; default: if (text == 0) { mc->status |= MAC_PARSE_UNDEF; } else if (*text == 0) { /* void */ ; } else if (mc->flags & MAC_EXP_FLAG_RECURSE) { mac_parse(text, mac_expand_callback, (char *) mc); } else { len = VSTRING_LEN(mc->result); vstring_strcat(mc->result, text); if (mc->filter) { cp = vstring_str(mc->result) + len; while (*(cp += strspn(cp, mc->filter))) *cp++ = '_'; } } break; } } /* * Literal text. */ else { text = vstring_str(buf); vstring_strcat(mc->result, text); } /* * Give the poor tester a clue of what is going on. */ if (msg_verbose) msg_info("%s: %s = %s", myname, vstring_str(buf), text ? text : "(undef)"); mc->level--; return (mc->status); }
void rewrite_tree(RWR_CONTEXT *context, TOK822 *tree) { TOK822 *colon; TOK822 *domain; TOK822 *bang; TOK822 *local; VSTRING *vstringval; /* * XXX If you change this module, quote_822_local.c, or tok822_parse.c, * be sure to re-run the tests under "make rewrite_clnt_test" and "make * resolve_clnt_test" in the global directory. */ /* * Sanity check. */ if (tree->head == 0) msg_panic("rewrite_tree: empty tree"); /* * An empty address is a special case. */ if (tree->head == tree->tail && tree->tail->type == TOK822_QSTRING && VSTRING_LEN(tree->tail->vstr) == 0) return; /* * Treat a lone @ as if it were an empty address. */ if (tree->head == tree->tail && tree->tail->type == '@') { tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); tok822_sub_append(tree, tok822_alloc(TOK822_QSTRING, "")); return; } /* * Strip source route. */ if (tree->head->type == '@' && (colon = tok822_find_type(tree->head, ':')) != 0 && colon != tree->tail) tok822_free_tree(tok822_sub_keep_after(tree, colon)); /* * Optionally, transform address forms without @. */ if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) { /* * Swap domain!user to user@domain. */ if (var_swap_bangpath != 0 && (bang = tok822_find_type(tree->head, '!')) != 0) { tok822_sub_keep_before(tree, bang); local = tok822_cut_after(bang); tok822_free(bang); tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0)); if (local) tok822_sub_prepend(tree, local); } /* * Promote user%domain to user@domain. */ else if (var_percent_hack != 0 && (domain = tok822_rfind_type(tree->tail, '%')) != 0) { domain->type = '@'; } /* * Append missing @origin */ else if (var_append_at_myorigin != 0 && REW_PARAM_VALUE(context->origin) != 0 && REW_PARAM_VALUE(context->origin)[0] != 0) { domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->origin), (TOK822 **) 0)); } } /* * Append missing .domain, but leave broken forms ending in @ alone. This * merely makes diagnostics more accurate by leaving bogus addresses * alone. * * Backwards-compatibility warning: warn for "user@localhost" when there is * no "localhost" in mydestination or in any other address class with an * explicit domain list. */ if (var_append_dot_mydomain != 0 && REW_PARAM_VALUE(context->domain) != 0 && REW_PARAM_VALUE(context->domain)[0] != 0 && (domain = tok822_rfind_type(tree->tail, '@')) != 0 && domain != tree->tail && tok822_find_type(domain, TOK822_DOMLIT) == 0 && tok822_find_type(domain, '.') == 0) { if (warn_compat_break_app_dot_mydomain && (vstringval = domain->next->vstr) != 0) { if (strcasecmp(vstring_str(vstringval), "localhost") != 0) { msg_info("using backwards-compatible default setting " VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to " "\"%s.%s\"", vstring_str(vstringval), vstring_str(vstringval), var_mydomain); } else if (resolve_class("localhost") == RESOLVE_CLASS_DEFAULT) { msg_info("using backwards-compatible default setting " VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to " "\"%s.%s\"; please add \"localhost\" to " "mydestination or other address class", vstring_str(vstringval), vstring_str(vstringval), var_mydomain); } } tok822_sub_append(tree, tok822_alloc('.', (char *) 0)); tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->domain), (TOK822 **) 0)); } /* * Strip trailing dot at end of domain, but not dot-dot or @-dot. This * merely makes diagnostics more accurate by leaving bogus addresses * alone. */ if (tree->tail->type == '.' && tree->tail->prev && tree->tail->prev->type != '.' && tree->tail->prev->type != '@') tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); }
char *casefoldx(int flags, VSTRING *dest, const char *src, ssize_t len) { size_t old_len; #ifdef NO_EAI /* * ASCII mode only. */ if (len < 0) len = strlen(src); if ((flags & CASEF_FLAG_APPEND) == 0) VSTRING_RESET(dest); old_len = VSTRING_LEN(dest); vstring_strncat(dest, src, len); lowercase(STR(dest) + old_len); return (STR(dest)); #else /* * Unicode mode. */ const char myname[] = "casefold"; static VSTRING *fold_buf = 0; static UCaseMap *csm = 0; UErrorCode error; ssize_t space_needed; int n; /* * Handle special cases. */ if (len < 0) len = strlen(src); if (dest == 0) dest = (fold_buf != 0 ? fold_buf : (fold_buf = vstring_alloc(100))); if ((flags & CASEF_FLAG_APPEND) == 0) VSTRING_RESET(dest); old_len = VSTRING_LEN(dest); /* * All-ASCII input, or ASCII mode only. */ if ((flags & CASEF_FLAG_UTF8) == 0 || allascii(src)) { vstring_strncat(dest, src, len); lowercase(STR(dest) + old_len); return (STR(dest)); } /* * ICU 4.8 ucasemap_utf8FoldCase() does not complain about UTF-8 syntax * errors. XXX Based on source-code review we conclude that non-UTF-8 * bytes are copied verbatim, and experiments confirm this. Given that * this behavior is intentional, we assume that it will stay that way. */ #if 0 if (valid_utf8_string(src, len) == 0) { if (err) *err = "malformed UTF-8 or invalid codepoint"; return (0); } #endif /* * One-time initialization. With ICU 4.8 this works while chrooted. */ if (csm == 0) { error = U_ZERO_ERROR; csm = ucasemap_open("en_US", U_FOLD_CASE_DEFAULT, &error); if (U_SUCCESS(error) == 0) msg_fatal("ucasemap_open error: %s", u_errorName(error)); } /* * Fold the input, adjusting the buffer size if needed. Safety: don't * loop forever. * * Note: the requested amount of space for casemapped output (as reported * with space_needed below) does not include storage for the null * terminator. The terminator is written only when the output buffer is * large enough. This is why we overallocate space when the output does * not fit. But if the output fits exactly, then the ouput will be * unterminated, and we have to terminate the output ourselves. */ for (n = 0; n < 3; n++) { error = U_ZERO_ERROR; space_needed = ucasemap_utf8FoldCase(csm, STR(dest) + old_len, vstring_avail(dest), src, len, &error); if (U_SUCCESS(error)) { VSTRING_AT_OFFSET(dest, old_len + space_needed); if (vstring_avail(dest) == 0) /* exact fit, no terminator */ VSTRING_TERMINATE(dest); /* add terminator */ break; } else if (error == U_BUFFER_OVERFLOW_ERROR) { VSTRING_SPACE(dest, space_needed + 1); /* for terminator */ } else { msg_fatal("%s: conversion error for \"%s\": %s", myname, src, u_errorName(error)); } } return (STR(dest)); #endif /* NO_EAI */ }
static void qmgr_deliver_update(int unused_event, char *context) { QMGR_ENTRY *entry = (QMGR_ENTRY *) context; QMGR_QUEUE *queue = entry->queue; QMGR_TRANSPORT *transport = queue->transport; QMGR_MESSAGE *message = entry->message; static DSN_BUF *dsb; int status; /* * Release the delivery agent from a "hot" queue entry. */ #define QMGR_DELIVER_RELEASE_AGENT(entry) do { \ event_disable_readwrite(vstream_fileno(entry->stream)); \ (void) vstream_fclose(entry->stream); \ entry->stream = 0; \ qmgr_deliver_concurrency--; \ } while (0) if (dsb == 0) dsb = dsb_create(); /* * The message transport has responded. Stop the watchdog timer. */ event_cancel_timer(qmgr_deliver_abort, context); /* * Retrieve the delivery agent status report. The numerical status code * indicates if delivery should be tried again. The reason text is sent * only when a site should be avoided for a while, so that the queue * manager can log why it does not even try to schedule delivery to the * affected recipients. */ status = qmgr_deliver_final_reply(entry->stream, dsb); /* * The mail delivery process failed for some reason (although delivery * may have been successful). Back off with this transport type for a * while. Dispose of queue entries for this transport that await * selection (the todo lists). Stay away from queue entries that have * been selected (the busy lists), or we would have dangling pointers. * The queue itself won't go away before we dispose of the current queue * entry. */ if (status == DELIVER_STAT_CRASH) { message->flags |= DELIVER_STAT_DEFER; #if 0 whatsup = concatenate("unknown ", transport->name, " mail transport error", (char *) 0); qmgr_transport_throttle(transport, DSN_SIMPLE(&dsb->dsn, "4.3.0", whatsup)); myfree(whatsup); #else qmgr_transport_throttle(transport, DSN_SIMPLE(&dsb->dsn, "4.3.0", "unknown mail transport error")); #endif msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description", transport->name); /* * Assume the worst and write a defer logfile record for each * recipient. This omission was already present in the first queue * manager implementation of 199703, and was fixed 200511. * * To avoid the synchronous qmgr_defer_recipient() operation for each * recipient of this queue entry, release the delivery process and * move the entry back to the todo queue. Let qmgr_defer_transport() * log the recipient asynchronously if possible, and get out of here. * Note: if asynchronous logging is not possible, * qmgr_defer_transport() eventually invokes qmgr_entry_done() and * the entry becomes a dangling pointer. */ QMGR_DELIVER_RELEASE_AGENT(entry); qmgr_entry_unselect(queue, entry); qmgr_defer_transport(transport, &dsb->dsn); return; } /* * This message must be tried again. * * If we have a problem talking to this site, back off with this site for a * while; dispose of queue entries for this site that await selection * (the todo list); stay away from queue entries that have been selected * (the busy list), or we would have dangling pointers. The queue itself * won't go away before we dispose of the current queue entry. * * XXX Caution: DSN_COPY() will panic on empty status or reason. */ #define SUSPENDED "delivery temporarily suspended: " if (status == DELIVER_STAT_DEFER) { message->flags |= DELIVER_STAT_DEFER; if (VSTRING_LEN(dsb->status)) { /* Sanitize the DSN status/reason from the delivery agent. */ if (!dsn_valid(vstring_str(dsb->status))) vstring_strcpy(dsb->status, "4.0.0"); if (VSTRING_LEN(dsb->reason) == 0) vstring_strcpy(dsb->reason, "unknown error"); vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1); if (QMGR_QUEUE_READY(queue)) { qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb)); if (QMGR_QUEUE_THROTTLED(queue)) qmgr_defer_todo(queue, &dsb->dsn); } } } /* * No problems detected. Mark the transport and queue as alive. The queue * itself won't go away before we dispose of the current queue entry. */ if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) { qmgr_transport_unthrottle(transport); qmgr_queue_unthrottle(queue); } /* * Release the delivery process, and give some other queue entry a chance * to be delivered. When all recipients for a message have been tried, * decide what to do next with this message: defer, bounce, delete. */ QMGR_DELIVER_RELEASE_AGENT(entry); qmgr_entry_done(entry, QMGR_QUEUE_BUSY); }