diff options
author | Gilles Chehade <gilles@poolp.org> | 2020-05-22 14:35:25 +0200 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2020-05-22 14:35:25 +0200 |
commit | 054cadd5524759b57aefb39659356552091118d9 (patch) | |
tree | 8c5c4896773cf10306211ec44c6cadb88a5a2ed7 /smtpd/smtp_client.c | |
parent | Revert "moving smtpd to usr.sbin/smtpd to ease cherry-picking of upstream" (diff) | |
download | OpenSMTPD-054cadd5524759b57aefb39659356552091118d9.tar.xz OpenSMTPD-054cadd5524759b57aefb39659356552091118d9.zip |
move smtpd to usr.sbin to ease upstream cherry-picking
Diffstat (limited to 'smtpd/smtp_client.c')
-rw-r--r-- | smtpd/smtp_client.c | 923 |
1 files changed, 0 insertions, 923 deletions
diff --git a/smtpd/smtp_client.c b/smtpd/smtp_client.c deleted file mode 100644 index 8e146e1b..00000000 --- a/smtpd/smtp_client.c +++ /dev/null @@ -1,923 +0,0 @@ -/* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 eric Exp $ */ - -/* - * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <resolv.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "log.h" -#include "ioev.h" -#include "smtp.h" - -#define TRACE_SMTPCLT 2 -#define TRACE_IO 3 - -enum { - STATE_INIT = 0, - STATE_BANNER, - STATE_EHLO, - STATE_HELO, - STATE_LHLO, - STATE_STARTTLS, - STATE_AUTH, - STATE_AUTH_PLAIN, - STATE_AUTH_LOGIN, - STATE_AUTH_LOGIN_USER, - STATE_AUTH_LOGIN_PASS, - STATE_READY, - STATE_MAIL, - STATE_RCPT, - STATE_DATA, - STATE_BODY, - STATE_EOM, - STATE_RSET, - STATE_QUIT, - - STATE_LAST -}; - -#define base64_encode __b64_ntop -#define base64_decode __b64_pton - -#define FLAG_TLS 0x01 -#define FLAG_TLS_VERIFIED 0x02 - -#define SMTP_EXT_STARTTLS 0x01 -#define SMTP_EXT_PIPELINING 0x02 -#define SMTP_EXT_AUTH 0x04 -#define SMTP_EXT_AUTH_PLAIN 0x08 -#define SMTP_EXT_AUTH_LOGIN 0x10 -#define SMTP_EXT_DSN 0x20 -#define SMTP_EXT_SIZE 0x40 - -struct smtp_client { - void *tag; - struct smtp_params params; - - int state; - int flags; - int ext; - size_t ext_size; - - struct io *io; - char *reply; - size_t replysz; - - struct smtp_mail *mail; - int rcptidx; - int rcptok; -}; - -void log_trace_verbose(int); -void log_trace(int, const char *, ...) - __attribute__((format (printf, 2, 3))); - -static void smtp_client_io(struct io *, int, void *); -static void smtp_client_free(struct smtp_client *); -static void smtp_client_state(struct smtp_client *, int); -static void smtp_client_abort(struct smtp_client *, int, const char *); -static void smtp_client_cancel(struct smtp_client *, int, const char *); -static void smtp_client_sendcmd(struct smtp_client *, char *, ...); -static void smtp_client_sendbody(struct smtp_client *); -static int smtp_client_readline(struct smtp_client *); -static int smtp_client_replycat(struct smtp_client *, const char *); -static void smtp_client_response(struct smtp_client *, const char *); -static void smtp_client_mail_abort(struct smtp_client *); -static void smtp_client_mail_status(struct smtp_client *, const char *); -static void smtp_client_rcpt_status(struct smtp_client *, struct smtp_rcpt *, const char *); - -static const char *strstate[STATE_LAST] = { - "INIT", - "BANNER", - "EHLO", - "HELO", - "LHLO", - "STARTTLS", - "AUTH", - "AUTH_PLAIN", - "AUTH_LOGIN", - "AUTH_LOGIN_USER", - "AUTH_LOGIN_PASS", - "READY", - "MAIL", - "RCPT", - "DATA", - "BODY", - "EOM", - "RSET", - "QUIT", -}; - -struct smtp_client * -smtp_connect(const struct smtp_params *params, void *tag) -{ - struct smtp_client *proto; - - proto = calloc(1, sizeof *proto); - if (proto == NULL) - return NULL; - - memmove(&proto->params, params, sizeof(*params)); - proto->tag = tag; - proto->io = io_new(); - if (proto->io == NULL) { - free(proto); - return NULL; - } - io_set_callback(proto->io, smtp_client_io, proto); - io_set_timeout(proto->io, proto->params.timeout); - - if (io_connect(proto->io, proto->params.dst, proto->params.src) == -1) { - smtp_client_abort(proto, FAIL_CONN, io_error(proto->io)); - return NULL; - } - - return proto; -} - -void -smtp_cert_verified(struct smtp_client *proto, int verified) -{ - if (verified == CERT_OK) - proto->flags |= FLAG_TLS_VERIFIED; - - else if (proto->params.tls_verify) { - errno = EAUTH; - smtp_client_abort(proto, FAIL_CONN, - "Invalid server certificate"); - return; - } - - io_resume(proto->io, IO_IN); - - if (proto->state == STATE_INIT) - smtp_client_state(proto, STATE_BANNER); - else { - /* Clear extensions before re-issueing an EHLO command. */ - proto->ext = 0; - smtp_client_state(proto, STATE_EHLO); - } -} - -void -smtp_set_tls(struct smtp_client *proto, void *ctx) -{ - io_start_tls(proto->io, ctx); -} - -void -smtp_quit(struct smtp_client *proto) -{ - if (proto->state != STATE_READY) - fatalx("connection is not ready"); - - smtp_client_state(proto, STATE_QUIT); -} - -void -smtp_sendmail(struct smtp_client *proto, struct smtp_mail *mail) -{ - if (proto->state != STATE_READY) - fatalx("connection is not ready"); - - proto->mail = mail; - smtp_client_state(proto, STATE_MAIL); -} - -static void -smtp_client_free(struct smtp_client *proto) -{ - if (proto->mail) - fatalx("current task should have been deleted already"); - - smtp_closed(proto->tag, proto); - - if (proto->io) - io_free(proto->io); - - free(proto->reply); - free(proto); -} - -/* - * End the session immediatly. - */ -static void -smtp_client_abort(struct smtp_client *proto, int err, const char *reason) -{ - smtp_failed(proto->tag, proto, err, reason); - - if (proto->mail) - smtp_client_mail_abort(proto); - - smtp_client_free(proto); -} - -/* - * Properly close the session. - */ -static void -smtp_client_cancel(struct smtp_client *proto, int err, const char *reason) -{ - if (proto->mail) - fatal("not supposed to have a mail"); - - smtp_failed(proto->tag, proto, err, reason); - - smtp_client_state(proto, STATE_QUIT); -} - -static void -smtp_client_state(struct smtp_client *proto, int newstate) -{ - struct smtp_rcpt *rcpt; - char ibuf[LINE_MAX], obuf[LINE_MAX]; - size_t n; - int oldstate; - - if (proto->reply) - proto->reply[0] = '\0'; - - again: - oldstate = proto->state; - proto->state = newstate; - - log_trace(TRACE_SMTPCLT, "%p: %s -> %s", proto, - strstate[oldstate], - strstate[newstate]); - - /* don't try this at home! */ -#define smtp_client_state(_s, _st) do { newstate = _st; goto again; } while (0) - - switch (proto->state) { - case STATE_BANNER: - io_set_read(proto->io); - break; - - case STATE_EHLO: - smtp_client_sendcmd(proto, "EHLO %s", proto->params.helo); - break; - - case STATE_HELO: - smtp_client_sendcmd(proto, "HELO %s", proto->params.helo); - break; - - case STATE_LHLO: - smtp_client_sendcmd(proto, "LHLO %s", proto->params.helo); - break; - - case STATE_STARTTLS: - if (proto->params.tls_req == TLS_NO || proto->flags & FLAG_TLS) - smtp_client_state(proto, STATE_AUTH); - else if (proto->ext & SMTP_EXT_STARTTLS) - smtp_client_sendcmd(proto, "STARTTLS"); - else if (proto->params.tls_req == TLS_FORCE) - smtp_client_cancel(proto, FAIL_IMPL, - "TLS not supported by remote host"); - else - smtp_client_state(proto, STATE_AUTH); - break; - - case STATE_AUTH: - if (!proto->params.auth_user) - smtp_client_state(proto, STATE_READY); - else if ((proto->flags & FLAG_TLS) == 0) - smtp_client_cancel(proto, FAIL_IMPL, - "Authentication requires TLS"); - else if ((proto->ext & SMTP_EXT_AUTH) == 0) - smtp_client_cancel(proto, FAIL_IMPL, - "AUTH not supported by remote host"); - else if (proto->ext & SMTP_EXT_AUTH_PLAIN) - smtp_client_state(proto, STATE_AUTH_PLAIN); - else if (proto->ext & SMTP_EXT_AUTH_LOGIN) - smtp_client_state(proto, STATE_AUTH_LOGIN); - else - smtp_client_cancel(proto, FAIL_IMPL, - "No supported AUTH method"); - break; - - case STATE_AUTH_PLAIN: - (void)strlcpy(ibuf, "-", sizeof(ibuf)); - (void)strlcat(ibuf, proto->params.auth_user, sizeof(ibuf)); - if (strlcat(ibuf, ":", sizeof(ibuf)) >= sizeof(ibuf)) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - n = strlcat(ibuf, proto->params.auth_pass, sizeof(ibuf)); - if (n >= sizeof(ibuf)) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - *strchr(ibuf, ':') = '\0'; - ibuf[0] = '\0'; - if (base64_encode(ibuf, n, obuf, sizeof(obuf)) == -1) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - smtp_client_sendcmd(proto, "AUTH PLAIN %s", obuf); - explicit_bzero(ibuf, sizeof ibuf); - explicit_bzero(obuf, sizeof obuf); - break; - - case STATE_AUTH_LOGIN: - smtp_client_sendcmd(proto, "AUTH LOGIN"); - break; - - case STATE_AUTH_LOGIN_USER: - if (base64_encode(proto->params.auth_user, - strlen(proto->params.auth_user), obuf, - sizeof(obuf)) == -1) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - smtp_client_sendcmd(proto, "%s", obuf); - explicit_bzero(obuf, sizeof obuf); - break; - - case STATE_AUTH_LOGIN_PASS: - if (base64_encode(proto->params.auth_pass, - strlen(proto->params.auth_pass), obuf, - sizeof(obuf)) == -1) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - smtp_client_sendcmd(proto, "%s", obuf); - explicit_bzero(obuf, sizeof obuf); - break; - - case STATE_READY: - smtp_ready(proto->tag, proto); - break; - - case STATE_MAIL: - if (proto->ext & SMTP_EXT_DSN) - smtp_client_sendcmd(proto, "MAIL FROM:<%s>%s%s%s%s", - proto->mail->from, - proto->mail->dsn_ret ? " RET=" : "", - proto->mail->dsn_ret ? proto->mail->dsn_ret : "", - proto->mail->dsn_envid ? " ENVID=" : "", - proto->mail->dsn_envid ? proto->mail->dsn_envid : ""); - else - smtp_client_sendcmd(proto, "MAIL FROM:<%s>", - proto->mail->from); - break; - - case STATE_RCPT: - if (proto->rcptidx == proto->mail->rcptcount) { - smtp_client_state(proto, STATE_DATA); - break; - } - rcpt = &proto->mail->rcpt[proto->rcptidx]; - if (proto->ext & SMTP_EXT_DSN) - smtp_client_sendcmd(proto, "RCPT TO:<%s>%s%s%s%s", - rcpt->to, - rcpt->dsn_notify ? " NOTIFY=" : "", - rcpt->dsn_notify ? rcpt->dsn_notify : "", - rcpt->dsn_orcpt ? " ORCPT=" : "", - rcpt->dsn_orcpt ? rcpt->dsn_orcpt : ""); - else - smtp_client_sendcmd(proto, "RCPT TO:<%s>", rcpt->to); - break; - - case STATE_DATA: - if (proto->rcptok == 0) { - smtp_client_mail_abort(proto); - smtp_client_state(proto, STATE_RSET); - } - else - smtp_client_sendcmd(proto, "DATA"); - break; - - case STATE_BODY: - fseek(proto->mail->fp, 0, SEEK_SET); - smtp_client_sendbody(proto); - break; - - case STATE_EOM: - smtp_client_sendcmd(proto, "."); - break; - - case STATE_RSET: - smtp_client_sendcmd(proto, "RSET"); - break; - - case STATE_QUIT: - smtp_client_sendcmd(proto, "QUIT"); - break; - - default: - fatalx("%s: bad state %d", __func__, proto->state); - } -#undef smtp_client_state -} - -/* - * Handle a response to an SMTP command - */ -static void -smtp_client_response(struct smtp_client *proto, const char *line) -{ - struct smtp_rcpt *rcpt; - int i, seen; - - switch (proto->state) { - case STATE_BANNER: - if (line[0] != '2') - smtp_client_abort(proto, FAIL_RESP, line); - else if (proto->params.lmtp) - smtp_client_state(proto, STATE_LHLO); - else - smtp_client_state(proto, STATE_EHLO); - break; - - case STATE_EHLO: - if (line[0] != '2') { - /* - * Either rejected or not implemented. If we want to - * use EHLO extensions, report an SMTP error. - * Otherwise, fallback to using HELO. - */ - if ((proto->params.tls_req == TLS_FORCE) || - (proto->params.auth_user)) - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_HELO); - break; - } - smtp_client_state(proto, STATE_STARTTLS); - break; - - case STATE_HELO: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_LHLO: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_STARTTLS: - if (line[0] != '2') { - if ((proto->params.tls_req == TLS_FORCE) || - (proto->params.auth_user)) { - smtp_client_cancel(proto, FAIL_RESP, line); - break; - } - smtp_client_state(proto, STATE_AUTH); - } - else - smtp_require_tls(proto->tag, proto); - break; - - case STATE_AUTH_PLAIN: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_AUTH_LOGIN: - if (strncmp(line, "334 ", 4)) - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_AUTH_LOGIN_USER); - break; - - case STATE_AUTH_LOGIN_USER: - if (strncmp(line, "334 ", 4)) - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_AUTH_LOGIN_PASS); - break; - - case STATE_AUTH_LOGIN_PASS: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_MAIL: - if (line[0] != '2') { - smtp_client_mail_status(proto, line); - smtp_client_state(proto, STATE_RSET); - } - else - smtp_client_state(proto, STATE_RCPT); - break; - - case STATE_RCPT: - rcpt = &proto->mail->rcpt[proto->rcptidx++]; - if (line[0] != '2') - smtp_client_rcpt_status(proto, rcpt, line); - else { - proto->rcptok++; - smtp_client_state(proto, STATE_RCPT); - } - break; - - case STATE_DATA: - if (line[0] != '2' && line[0] != '3') { - smtp_client_mail_status(proto, line); - smtp_client_state(proto, STATE_RSET); - } - else - smtp_client_state(proto, STATE_BODY); - break; - - case STATE_EOM: - if (proto->params.lmtp) { - /* - * LMTP reports a status of each accepted RCPT. - * Report status for the first pending RCPT and read - * more lines if another rcpt needs a status. - */ - for (i = 0, seen = 0; i < proto->mail->rcptcount; i++) { - rcpt = &proto->mail->rcpt[i]; - if (rcpt->done) - continue; - if (seen) { - io_set_read(proto->io); - return; - } - smtp_client_rcpt_status(proto, - &proto->mail->rcpt[i], line); - seen = 1; - } - } - smtp_client_mail_status(proto, line); - smtp_client_state(proto, STATE_READY); - break; - - case STATE_RSET: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_QUIT: - smtp_client_free(proto); - break; - - default: - fatalx("%s: bad state %d", __func__, proto->state); - } -} - -static void -smtp_client_io(struct io *io, int evt, void *arg) -{ - struct smtp_client *proto = arg; - - log_trace(TRACE_IO, "%p: %s %s", proto, io_strevent(evt), io_strio(io)); - - switch (evt) { - case IO_CONNECTED: - if (proto->params.tls_req == TLS_SMTPS) { - io_set_write(io); - smtp_require_tls(proto->tag, proto); - } - else - smtp_client_state(proto, STATE_BANNER); - break; - - case IO_TLSREADY: - proto->flags |= FLAG_TLS; - io_pause(proto->io, IO_IN); - smtp_verify_server_cert(proto->tag, proto, io_tls(proto->io)); - break; - - case IO_DATAIN: - while (smtp_client_readline(proto)) - ; - break; - - case IO_LOWAT: - if (proto->state == STATE_BODY) - smtp_client_sendbody(proto); - else - io_set_read(io); - break; - - case IO_TIMEOUT: - errno = ETIMEDOUT; - smtp_client_abort(proto, FAIL_CONN, "Connection timeout"); - break; - - case IO_ERROR: - smtp_client_abort(proto, FAIL_CONN, io_error(io)); - break; - - case IO_TLSERROR: - smtp_client_abort(proto, FAIL_CONN, io_error(io)); - break; - - case IO_DISCONNECTED: - smtp_client_abort(proto, FAIL_CONN, io_error(io)); - break; - - default: - fatalx("%s: bad event %d", __func__, evt); - } -} - -/* - * return 1 if a new line is expected. - */ -static int -smtp_client_readline(struct smtp_client *proto) -{ - const char *e; - size_t len; - char *line, *msg, *p; - int cont; - - line = io_getline(proto->io, &len); - if (line == NULL) { - if (io_datalen(proto->io) >= proto->params.linemax) - smtp_client_abort(proto, FAIL_PROTO, "Line too long"); - return 0; - } - - /* Strip trailing '\r' */ - if (len && line[len - 1] == '\r') - line[--len] = '\0'; - - log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line); - - /* Validate SMTP */ - if (len > 3) { - msg = line + 4; - cont = (line[3] == '-'); - } else if (len == 3) { - msg = line + 3; - cont = 0; - } else { - smtp_client_abort(proto, FAIL_PROTO, "Response too short"); - return 0; - } - - /* Validate reply code. */ - if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) || - !isdigit((unsigned char)line[2])) { - smtp_client_abort(proto, FAIL_PROTO, "Invalid reply code"); - return 0; - } - - /* Validate reply message. */ - for (p = msg; *p; p++) - if (!isprint((unsigned char)*p)) { - smtp_client_abort(proto, FAIL_PROTO, - "Non-printable characters in response"); - return 0; - } - - /* Read extensions. */ - if (proto->state == STATE_EHLO) { - if (strcmp(msg, "STARTTLS") == 0) - proto->ext |= SMTP_EXT_STARTTLS; - else if (strncmp(msg, "AUTH ", 5) == 0) { - proto->ext |= SMTP_EXT_AUTH; - if ((p = strstr(msg, " PLAIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - proto->ext |= SMTP_EXT_AUTH_PLAIN; - if ((p = strstr(msg, " LOGIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - proto->ext |= SMTP_EXT_AUTH_LOGIN; - } - else if (strcmp(msg, "PIPELINING") == 0) - proto->ext |= SMTP_EXT_PIPELINING; - else if (strcmp(msg, "DSN") == 0) - proto->ext |= SMTP_EXT_DSN; - else if (strncmp(msg, "SIZE ", 5) == 0) { - proto->ext_size = strtonum(msg + 5, 0, SIZE_T_MAX, &e); - if (e == NULL) - proto->ext |= SMTP_EXT_SIZE; - } - } - - if (smtp_client_replycat(proto, line) == -1) { - smtp_client_abort(proto, FAIL_INTERNAL, NULL); - return 0; - } - - if (cont) - return 1; - - if (io_datalen(proto->io)) { - /* - * There should be no pending data after a response is read, - * except for the multiple status lines after a LMTP message. - * It can also happen with pipelineing, but we don't do that - * for now. - */ - if (!(proto->params.lmtp && proto->state == STATE_EOM)) { - smtp_client_abort(proto, FAIL_PROTO, "Trailing data"); - return 0; - } - } - - io_set_write(proto->io); - smtp_client_response(proto, proto->reply); - return 0; -} - -/* - * Concatenate the given response line. - */ -static int -smtp_client_replycat(struct smtp_client *proto, const char *line) -{ - size_t len; - char *tmp; - int first; - - if (proto->reply && proto->reply[0]) { - /* - * If the line is the continuation of an multi-line response, - * skip the status and ESC parts. First, skip the status, then - * skip the separator amd ESC if found. - */ - first = 0; - line += 3; - if (line[0]) { - line += 1; - if (isdigit((unsigned char)line[0]) && line[1] == '.' && - isdigit((unsigned char)line[2]) && line[3] == '.' && - isdigit((unsigned char)line[4]) && - isspace((unsigned char)line[5])) - line += 5; - } - } else - first = 1; - - if (proto->reply) { - len = strlcat(proto->reply, line, proto->replysz); - if (len < proto->replysz) - return 0; - } - else - len = strlen(line); - - if (len > proto->params.ibufmax) { - errno = EMSGSIZE; - return -1; - } - - /* Allocate by multiples of 2^8 */ - len += (len % 256) ? (256 - (len % 256)) : 0; - - tmp = realloc(proto->reply, len); - if (tmp == NULL) - return -1; - if (proto->reply == NULL) - tmp[0] = '\0'; - - proto->reply = tmp; - proto->replysz = len; - (void)strlcat(proto->reply, line, proto->replysz); - - /* Replace the separator with a space for the first line. */ - if (first && proto->reply[3]) - proto->reply[3] = ' '; - - return 0; -} - -static void -smtp_client_sendbody(struct smtp_client *proto) -{ - ssize_t len; - size_t sz = 0, total, w; - char *ln = NULL; - int n; - - total = io_queued(proto->io); - w = 0; - - while (total < proto->params.obufmax) { - if ((len = getline(&ln, &sz, proto->mail->fp)) == -1) - break; - if (ln[len - 1] == '\n') - ln[len - 1] = '\0'; - n = io_printf(proto->io, "%s%s\r\n", *ln == '.'?".":"", ln); - if (n == -1) { - free(ln); - smtp_client_abort(proto, FAIL_INTERNAL, NULL); - return; - } - total += n; - w += n; - } - free(ln); - - if (ferror(proto->mail->fp)) { - smtp_client_abort(proto, FAIL_INTERNAL, "Cannot read message"); - return; - } - - log_trace(TRACE_SMTPCLT, "%p: >>> [...%zd bytes...]", proto, w); - - if (feof(proto->mail->fp)) - smtp_client_state(proto, STATE_EOM); -} - -static void -smtp_client_sendcmd(struct smtp_client *proto, char *fmt, ...) -{ - va_list ap; - char *p; - int len; - - va_start(ap, fmt); - len = vasprintf(&p, fmt, ap); - va_end(ap); - - if (len == -1) { - smtp_client_abort(proto, FAIL_INTERNAL, NULL); - return; - } - - log_trace(TRACE_SMTPCLT, "mta: %p: >>> %s", proto, p); - - len = io_printf(proto->io, "%s\r\n", p); - free(p); - - if (len == -1) - smtp_client_abort(proto, FAIL_INTERNAL, NULL); -} - -static void -smtp_client_mail_status(struct smtp_client *proto, const char *status) -{ - int i; - - for (i = 0; i < proto->mail->rcptcount; i++) - smtp_client_rcpt_status(proto, &proto->mail->rcpt[i], status); - - smtp_done(proto->tag, proto, proto->mail); - proto->mail = NULL; -} - -static void -smtp_client_mail_abort(struct smtp_client *proto) -{ - smtp_done(proto->tag, proto, proto->mail); - proto->mail = NULL; -} - -static void -smtp_client_rcpt_status(struct smtp_client *proto, struct smtp_rcpt *rcpt, const char *line) -{ - struct smtp_status status; - - if (rcpt->done) - return; - - rcpt->done = 1; - status.rcpt = rcpt; - status.cmd = strstate[proto->state]; - status.status = line; - smtp_status(proto->tag, proto, &status); -} |