diff options
author | Gilles Chehade <gilles@poolp.org> | 2014-11-02 22:55:51 +0100 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2014-11-02 22:55:51 +0100 |
commit | 7e03a329963863764917e0f4319e4ed8c5003db7 (patch) | |
tree | c39f593fc3301719fa723d33f96fa96e228d4829 | |
parent | Merge branch 'master' of ssh://ssh.poolp.org/git/opensmtpd (diff) | |
download | OpenSMTPD-7e03a329963863764917e0f4319e4ed8c5003db7.tar.xz OpenSMTPD-7e03a329963863764917e0f4319e4ed8c5003db7.zip |
bypass the rfc822 parser, append domain can be achieved in a much simpler way
-rw-r--r-- | smtpd/smtp_session.c | 225 |
1 files changed, 167 insertions, 58 deletions
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c index 4f61f3da..447cf124 100644 --- a/smtpd/smtp_session.c +++ b/smtpd/smtp_session.c @@ -49,6 +49,8 @@ #define DATA_HIWAT 65535 +#define APPEND_DOMAIN_BUFFER_SIZE 4096 + enum smtp_phase { PHASE_INIT = 0, PHASE_SETUP, @@ -282,74 +284,181 @@ header_bcc_callback(const struct rfc2822_header *hdr, void *arg) } static void -header_masquerade_callback(const struct rfc2822_header *hdr, void *arg) +header_append_domain_buffer(char *buffer, char *domain, size_t len) { - struct smtp_session *s = arg; - struct rfc822_parser rp; - struct rfc2822_line *l; - struct rfc822_address *ra; - size_t len; - char buf[SMTPD_MAXLINESIZE]; - int ret; + size_t i; + int escape, quote, comment, bracket; + int has_domain, has_bracket, has_group; + int pos_bracket, pos_component, pos_insert; + char copy[APPEND_DOMAIN_BUFFER_SIZE]; + + i = 0; + escape = quote = comment = bracket = 0; + has_domain = has_bracket = has_group = 0; + pos_bracket = pos_insert = pos_component = 0; + for (i = 0; buffer[i]; ++i) { + if (buffer[i] == '(' && !escape && !quote) + comment++; + if (buffer[i] == '"' && !escape && !comment) + quote = !quote; + if (buffer[i] == ')' && !escape && !quote && comment) + comment--; + if (buffer[i] == '\\' && !escape && !comment && !quote) + escape = 1; + else + escape = 0; + if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) { + bracket++; + has_bracket = 1; + } + if (buffer[i] == '>' && !escape && !comment && !quote && bracket) { + bracket--; + pos_bracket = i; + } + if (buffer[i] == '@' && !escape && !comment && !quote) + has_domain = 1; + if (buffer[i] == ':' && !escape && !comment && !quote) + has_group = 1; + if (! isspace(buffer[i])) + pos_component = i; + } - rfc822_parser_init(&rp); - TAILQ_FOREACH(l, &hdr->lines, next) - if (! rfc822_parser_feed(&rp, l->buffer)) - goto fail; + /* parse error, do not attempt to modify */ + if (escape || quote || comment || bracket) + return; - len = strlen(hdr->name) + 1; - if (iobuf_fqueue(&s->obuf, "%s:", hdr->name) != (int)len) { - s->msgflags |= MF_ERROR_IO; - rfc822_parser_reset(&rp); - return; + /* domain already present, no need to modify */ + if (has_domain) + return; + + /* address is group, skip */ + if (has_group) + return; + + /* there's an address between brackets, just append domain */ + if (has_bracket) { + pos_bracket--; + while (isspace(buffer[pos_bracket])) + pos_bracket--; + if (buffer[pos_bracket] == '<') + return; + pos_insert = pos_bracket + 1; } - s->odatalen += len; + else { + /* otherwise append address to last component */ + pos_insert = pos_component + 1; - TAILQ_FOREACH(ra, &rp.addresses, next) { - /* no address provided, append local host */ - if (strchr(ra->address, '@') == NULL) { - (void)strlcat(ra->address, "@", sizeof ra->address); - if (strlcat(ra->address, s->listener->hostname, - sizeof ra->address) >= sizeof ra->address) { - s->msgflags |= MF_ERROR_MALFORMED; - rfc822_parser_reset(&rp); - return; + /* empty address */ + if (buffer[pos_component] == '\0' || isspace(buffer[pos_component])) + return; + } + + if (snprintf(copy, sizeof copy, "%.*s@%s%s", + (int)pos_insert, buffer, + domain, + buffer+pos_insert) >= (int)sizeof copy) + return; + + memcpy(buffer, copy, len); +} + +static void +header_masquerade_callback(const struct rfc2822_header *hdr, void *arg) +{ + struct smtp_session *s = arg; + struct rfc2822_line *l; + size_t i, j; + int escape, quote, comment, skip; + size_t len; + char buffer[APPEND_DOMAIN_BUFFER_SIZE]; + + len = strlen(hdr->name) + 1; + if (iobuf_fqueue(&s->obuf, "%s:", hdr->name) != (int)len) + goto ioerror; + s->odatalen += len; + + i = j = 0; + escape = quote = comment = skip = 0; + memset(buffer, 0, sizeof buffer); + + TAILQ_FOREACH(l, &hdr->lines, next) { + for (i = 0; i < strlen(l->buffer); ++i) { + if (l->buffer[i] == '(' && !escape && !quote) + comment++; + if (l->buffer[i] == '"' && !escape && !comment) + quote = !quote; + if (l->buffer[i] == ')' && !escape && !quote && comment) + comment--; + if (l->buffer[i] == '\\' && !escape && !comment && !quote) + escape = 1; + else + escape = 0; + + /* found a separator, buffer contains a full address */ + if (l->buffer[i] == ',' && !escape && !quote && !comment) { + if (!skip && j + strlen(s->listener->hostname) + 1 < sizeof buffer) + header_append_domain_buffer(buffer, s->listener->hostname, sizeof buffer); + len = strlen(buffer) + 1; + if (iobuf_fqueue(&s->obuf, "%s,", buffer) != (int)len) + goto ioerror; + s->odatalen += len; + j = 0; + skip = 0; + memset(buffer, 0, sizeof buffer); + } + else { + if (skip) { + if (iobuf_fqueue(&s->obuf, "%c", l->buffer[i]) != (int)1) + goto ioerror; + s->odatalen += 1; + } + else { + buffer[j++] = l->buffer[i]; + if (j == sizeof (buffer) - 1) { + len = strlen(buffer); + if (iobuf_fqueue(&s->obuf, "%s", buffer) != (int)len) + goto ioerror; + s->odatalen += len; + skip = 1; + j = 0; + memset(buffer, 0, sizeof buffer); + } + } } - } - - if (ra->name[0] == '\0') { - ret = snprintf(buf, sizeof buf, "%s%s%s", - TAILQ_FIRST(&rp.addresses) == ra ? " " : "\t", - ra->address, - TAILQ_LAST(&rp.addresses, addresses) == ra ? "" : ","); - } - else { - ret = snprintf(buf, sizeof buf, "%s%s <%s>%s", - TAILQ_FIRST(&rp.addresses) == ra ? " " : "\t", - ra->name, - ra->address, - TAILQ_LAST(&rp.addresses, addresses) == ra ? "" : ","); - } - if (ret == -1 || ret >= (int)sizeof buf) { - s->msgflags |= MF_ERROR_MALFORMED; - rfc822_parser_reset(&rp); - return; } - - len = strlen(buf) + 1; - if (iobuf_fqueue(&s->obuf, "%s\n", buf) != (int)len) { - s->msgflags |= MF_ERROR_IO; - rfc822_parser_reset(&rp); - return; + if (skip) { + if (iobuf_fqueue(&s->obuf, "\n", l->buffer[i]) != (int)1) + goto ioerror; + s->odatalen += 1; + } + else { + buffer[j++] = '\n'; + if (j == sizeof (buffer) - 1) { + len = strlen(buffer); + if (iobuf_fqueue(&s->obuf, "%s", buffer) != (int)len) + goto ioerror; + s->odatalen += len; + skip = 1; + j = 0; + memset(buffer, 0, sizeof buffer); + } } + } + + /* end of header, if buffer is not empty we'll process it */ + if (buffer[0]) { + if (j + strlen(s->listener->hostname) + 1 < sizeof buffer) + header_append_domain_buffer(buffer, s->listener->hostname, sizeof buffer); + len = strlen(buffer); + if (iobuf_fqueue(&s->obuf, "%s", buffer) != (int)len) + goto ioerror; s->odatalen += len; - } - rfc822_parser_reset(&rp); - return; + } + return; -fail: - rfc822_parser_reset(&rp); - header_default_callback(hdr, arg); +ioerror: + s->msgflags |= MF_ERROR_IO; + return; } static void |