Files
bongo/src/agents/smtp/protocol.c
T
Mario Fetka 3209fc32dd
Debian Trixie package bundle / packages (push) Successful in 15m43s
smtp: reject an empty recipient path
2026-07-28 07:18:50 +02:00

920 lines
30 KiB
C

/****************************************************************************
* <Novell-copyright>
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail, you
* may find current contact information at www.novell.com.
* </Novell-copyright>
****************************************************************************/
#include "protocol.h"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <nmap.h>
typedef struct {
const char *name;
SMTPCommand command;
} SMTPCommandEntry;
static const SMTPCommandEntry Commands[] = {
{ "AUTH", SMTP_COMMAND_AUTH },
{ "BDAT", SMTP_COMMAND_BDAT },
{ "DATA", SMTP_COMMAND_DATA },
{ "EHLO", SMTP_COMMAND_EHLO },
{ "ETRN", SMTP_COMMAND_ETRN },
{ "EXPN", SMTP_COMMAND_EXPN },
{ "HELO", SMTP_COMMAND_HELO },
{ "HELP", SMTP_COMMAND_HELP },
{ "MAIL", SMTP_COMMAND_MAIL },
{ "NOOP", SMTP_COMMAND_NOOP },
{ "QUIT", SMTP_COMMAND_QUIT },
{ "RCPT", SMTP_COMMAND_RCPT },
{ "RSET", SMTP_COMMAND_RSET },
{ "SAML", SMTP_COMMAND_SAML },
{ "SEND", SMTP_COMMAND_SEND },
{ "SOML", SMTP_COMMAND_SOML },
{ "STARTTLS", SMTP_COMMAND_STARTTLS },
{ "VRFY", SMTP_COMMAND_VRFY }
};
static int
CommandEquals(const char *command, size_t length, const char *expected)
{
size_t i;
if (strlen(expected) != length) return 0;
for (i = 0; i < length; i++) {
if (toupper((unsigned char)command[i]) !=
(unsigned char)expected[i]) return 0;
}
return 1;
}
int
SMTPRequireTlsHasAuthenticatedPolicy(int mx_dnssec_authenticated,
int dane_authenticated,
int mta_sts_enforced,
int web_pki_verification)
{
return dane_authenticated || mta_sts_enforced ||
(mx_dnssec_authenticated && web_pki_verification);
}
SMTPCommand
SMTPParseCommandLine(const char *line, size_t line_length,
const char **arguments)
{
const char *cursor;
size_t command_length;
size_t i;
if (arguments) *arguments = NULL;
if (!line || line_length == 0 || strlen(line) != line_length ||
*line == ' ' || *line == '\t')
return SMTP_COMMAND_UNKNOWN;
for (i = 0; i < line_length; i++) {
unsigned char current = (unsigned char)line[i];
if ((current < 0x20U && current != '\t') || current == 0x7fU)
return SMTP_COMMAND_UNKNOWN;
}
cursor = line;
while (*cursor && *cursor != ' ' && *cursor != '\t') {
if ((unsigned char)*cursor < 0x21U ||
(unsigned char)*cursor > 0x7eU) return SMTP_COMMAND_UNKNOWN;
cursor++;
}
command_length = (size_t)(cursor - line);
while (*cursor == ' ' || *cursor == '\t') cursor++;
if (arguments) *arguments = cursor;
for (i = 0; i < sizeof(Commands) / sizeof(Commands[0]); i++) {
if (CommandEquals(line, command_length, Commands[i].name))
return Commands[i].command;
}
return SMTP_COMMAND_UNKNOWN;
}
int
SMTPArgumentsEmpty(const char *arguments)
{
return arguments != NULL && *arguments == '\0';
}
int
SMTPParseGreetingArgument(const char *arguments, char *domain,
size_t domain_size)
{
const unsigned char *cursor = (const unsigned char *)arguments;
size_t length = 0;
if (!arguments || !domain || domain_size == 0 || !*cursor) return 0;
while (*cursor) {
if (*cursor <= 0x20U || *cursor >= 0x7fU ||
length >= 255U || length + 1 >= domain_size) {
domain[0] = '\0';
return 0;
}
domain[length++] = (char)*cursor++;
}
domain[length] = '\0';
return 1;
}
int
SMTPParseBdatArguments(const char *arguments, uint64_t *chunk_size,
int *last_chunk)
{
const char *cursor = arguments;
uint64_t parsed = 0U;
if (chunk_size) *chunk_size = 0U;
if (last_chunk) *last_chunk = 0;
if (!cursor || !chunk_size || !last_chunk ||
!isdigit((unsigned char)*cursor)) return 0;
do {
unsigned int digit = (unsigned int)(*cursor - '0');
if (parsed > (UINT64_MAX - digit) / 10U) return 0;
parsed = parsed * 10U + digit;
cursor++;
} while (isdigit((unsigned char)*cursor));
if (*cursor != '\0' && *cursor != ' ' && *cursor != '\t') return 0;
while (*cursor == ' ' || *cursor == '\t') cursor++;
if (*cursor != '\0') {
const char *token = cursor;
while (*cursor != '\0' && *cursor != ' ' && *cursor != '\t')
cursor++;
if (!CommandEquals(token, (size_t)(cursor - token), "LAST")) return 0;
while (*cursor == ' ' || *cursor == '\t') cursor++;
if (*cursor != '\0') return 0;
*last_chunk = 1;
}
*chunk_size = parsed;
return 1;
}
int
SMTPParseVerifyArguments(const char *arguments, char *query,
size_t query_size, int *smtp_utf8)
{
const char *end;
const char *token;
size_t length;
size_t i;
if (query != NULL && query_size != 0U) query[0] = '\0';
if (smtp_utf8 != NULL) *smtp_utf8 = 0;
if (arguments == NULL || query == NULL || query_size == 0U ||
smtp_utf8 == NULL || *arguments == '\0') return 0;
end = arguments + strlen(arguments);
while (end > arguments && (end[-1] == ' ' || end[-1] == '\t')) end--;
if (end == arguments) return 0;
token = end;
while (token > arguments && token[-1] != ' ' && token[-1] != '\t')
token--;
if (token > arguments &&
CommandEquals(token, (size_t)(end - token), "SMTPUTF8")) {
*smtp_utf8 = 1;
end = token;
while (end > arguments && (end[-1] == ' ' || end[-1] == '\t')) end--;
if (end == arguments) return 0;
}
length = (size_t)(end - arguments);
if (length + 1U > query_size) return 0;
for (i = 0U; i < length; i++) {
unsigned char current = (unsigned char)arguments[i];
if ((current < 0x20U && current != '\t') || current == 0x7fU)
return 0;
}
memcpy(query, arguments, length);
query[length] = '\0';
return 1;
}
int
SMTPPathIsSafe(const char *path)
{
const unsigned char *cursor = (const unsigned char *)path;
if (cursor == NULL) return 0;
while (*cursor != '\0') {
if (*cursor < 0x21U || *cursor == 0x7fU ||
*cursor == '<' || *cursor == '>') return 0;
cursor++;
}
return 1;
}
int
SMTPRecipientPathIsValid(const char *path)
{
return path != NULL && path[0] != '\0' && SMTPPathIsSafe(path);
}
static int
HexValue(unsigned char value)
{
if (value >= '0' && value <= '9') return (int)(value - '0');
if (value >= 'A' && value <= 'F') return (int)(value - 'A') + 10;
if (value >= 'a' && value <= 'f') return (int)(value - 'a') + 10;
return -1;
}
int
SMTPXtextEncode(const char *value, char *encoded, size_t encoded_size)
{
static const char hex[] = "0123456789ABCDEF";
const unsigned char *cursor = (const unsigned char *)value;
size_t used = 0U;
if (encoded != NULL && encoded_size != 0U) encoded[0] = '\0';
if (value == NULL || encoded == NULL || encoded_size == 0U) return 0;
while (*cursor != '\0') {
if (*cursor >= 0x21U && *cursor <= 0x7eU &&
*cursor != '+' && *cursor != '=') {
if (used + 1U >= encoded_size) return 0;
encoded[used++] = (char)*cursor;
} else {
if (used + 3U >= encoded_size) return 0;
encoded[used++] = '+';
encoded[used++] = hex[*cursor >> 4U];
encoded[used++] = hex[*cursor & 0x0fU];
}
cursor++;
}
encoded[used] = '\0';
return 1;
}
int
SMTPXtextDecode(const char *encoded, char *value, size_t value_size)
{
const unsigned char *cursor = (const unsigned char *)encoded;
size_t used = 0U;
if (value != NULL && value_size != 0U) value[0] = '\0';
if (encoded == NULL || value == NULL || value_size == 0U) return 0;
while (*cursor != '\0') {
unsigned char decoded;
if (*cursor == '+') {
int high;
int low;
if (cursor[1] == '\0' || cursor[2] == '\0' ||
(high = HexValue(cursor[1])) < 0 ||
(low = HexValue(cursor[2])) < 0) return 0;
decoded = (unsigned char)((high << 4) | low);
cursor += 3;
} else {
if (*cursor < 0x21U || *cursor > 0x7eU || *cursor == '=')
return 0;
decoded = *cursor++;
}
if (decoded == '\0' || used + 1U >= value_size) return 0;
value[used++] = (char)decoded;
}
value[used] = '\0';
return 1;
}
static int
TokenEquals(const char *token, size_t length, const char *expected)
{
return CommandEquals(token, length, expected);
}
static int
TokenPrefix(const char *token, size_t length, const char *prefix,
const char **value, size_t *value_length)
{
size_t prefix_length = strlen(prefix);
if (length < prefix_length ||
!CommandEquals(token, prefix_length, prefix)) return 0;
*value = token + prefix_length;
*value_length = length - prefix_length;
return 1;
}
static int
NextParameter(const char **cursor, const char **token, size_t *length)
{
const char *current = *cursor;
if (!current) return 0;
while (*current == ' ' || *current == '\t') current++;
if (!*current) {
*cursor = current;
return 0;
}
*token = current;
while (*current && *current != ' ' && *current != '\t') current++;
*length = (size_t)(current - *token);
*cursor = current;
return 1;
}
static int
ParseSizeToken(const char *value, size_t length, uint64_t *size)
{
uint64_t parsed = 0;
size_t i;
if (!length) return 0;
for (i = 0; i < length; i++) {
unsigned int digit;
if (!isdigit((unsigned char)value[i])) return 0;
digit = (unsigned int)(value[i] - '0');
if (parsed > (UINT64_MAX - digit) / 10U) return 0;
parsed = parsed * 10U + digit;
}
*size = parsed;
return 1;
}
static int
HexDigit(unsigned char value)
{
return (value >= '0' && value <= '9') ||
(value >= 'A' && value <= 'F') ||
(value >= 'a' && value <= 'f');
}
static int
XtextValid(const char *value, size_t length, size_t maximum)
{
size_t i;
if (!length || length > maximum) return 0;
for (i = 0; i < length; i++) {
unsigned char current = (unsigned char)value[i];
if (current < 0x21U || current > 0x7eU || current == '=') return 0;
if (current == '+') {
unsigned int decoded;
if (length - i < 3U || !HexDigit((unsigned char)value[i + 1]) ||
!HexDigit((unsigned char)value[i + 2])) return 0;
decoded = (unsigned int)(
HexValue((unsigned char)value[i + 1]) * 16 +
HexValue((unsigned char)value[i + 2]));
if (decoded < 0x20U || decoded > 0x7eU) return 0;
i += 2U;
}
}
return 1;
}
SMTPParameterResult
SMTPParseMailParameters(const char *parameters, SMTPMailParameters *result)
{
const char *cursor = parameters;
const char *token;
const char *value;
size_t length;
size_t value_length;
if (!result) return SMTP_PARAMETER_SYNTAX;
memset(result, 0, sizeof(*result));
while (NextParameter(&cursor, &token, &length)) {
if (TokenEquals(token, length, "BODY=7BIT") ||
TokenEquals(token, length, "BODY=8BITMIME") ||
TokenEquals(token, length, "BODY=BINARYMIME")) {
if (result->body != SMTP_BODY_UNSPECIFIED)
return SMTP_PARAMETER_SYNTAX;
if (TokenEquals(token, length, "BODY=7BIT"))
result->body = SMTP_BODY_7BIT;
else if (TokenEquals(token, length, "BODY=8BITMIME"))
result->body = SMTP_BODY_8BITMIME;
else {
result->body = SMTP_BODY_BINARYMIME;
return SMTP_PARAMETER_BINARYMIME;
}
} else if (TokenPrefix(token, length, "SIZE=", &value,
&value_length)) {
if (result->size_supplied ||
!ParseSizeToken(value, value_length, &result->size))
return SMTP_PARAMETER_SYNTAX;
result->size_supplied = 1;
} else if (TokenEquals(token, length, "SMTPUTF8")) {
if (result->smtp_utf8) return SMTP_PARAMETER_SYNTAX;
result->smtp_utf8 = 1;
} else if (TokenEquals(token, length, "REQUIRETLS")) {
if (result->require_tls) return SMTP_PARAMETER_SYNTAX;
result->require_tls = 1;
} else if (TokenPrefix(token, length, "RET=", &value,
&value_length)) {
if (result->ret_supplied) return SMTP_PARAMETER_SYNTAX;
if (TokenEquals(value, value_length, "FULL"))
result->ret_full = 1;
else if (!TokenEquals(value, value_length, "HDRS"))
return SMTP_PARAMETER_SYNTAX;
result->ret_supplied = 1;
} else if (TokenPrefix(token, length, "ENVID=", &value,
&value_length)) {
if (result->envid_supplied ||
!XtextValid(value, value_length, SMTP_ENVID_MAX))
return SMTP_PARAMETER_SYNTAX;
memcpy(result->envid, value, value_length);
result->envid[value_length] = '\0';
result->envid_supplied = 1;
} else if (TokenPrefix(token, length, "AUTH=", &value,
&value_length)) {
if (result->auth_supplied ||
!XtextValid(value, value_length, SMTP_ORCPT_MAX))
return SMTP_PARAMETER_SYNTAX;
result->auth_supplied = 1;
} else {
return SMTP_PARAMETER_UNSUPPORTED;
}
}
return SMTP_PARAMETER_OK;
}
static int
ParseNotify(const char *value, size_t length, unsigned int *notify)
{
const char *cursor = value;
const char *end = value + length;
unsigned int flags = 0U;
while (cursor < end) {
const char *item = cursor;
size_t item_length;
unsigned int flag;
while (cursor < end && *cursor != ',') cursor++;
item_length = (size_t)(cursor - item);
if (TokenEquals(item, item_length, "SUCCESS"))
flag = SMTP_NOTIFY_SUCCESS;
else if (TokenEquals(item, item_length, "FAILURE"))
flag = SMTP_NOTIFY_FAILURE;
else if (TokenEquals(item, item_length, "DELAY"))
flag = SMTP_NOTIFY_DELAY;
else if (TokenEquals(item, item_length, "NEVER"))
flag = SMTP_NOTIFY_NEVER;
else
return 0;
if ((flags & flag) != 0U) return 0;
flags |= flag;
if (cursor < end) cursor++;
}
if (!flags || ((flags & SMTP_NOTIFY_NEVER) != 0U &&
flags != SMTP_NOTIFY_NEVER)) return 0;
*notify = flags;
return 1;
}
static int
Utf8CharacterLength(const unsigned char *value, size_t remaining,
size_t *character_length, unsigned int *decoded)
{
unsigned int codepoint;
size_t length;
size_t i;
if (remaining == 0U || value[0] < 0x80U) return 0;
if (value[0] >= 0xc2U && value[0] <= 0xdfU) {
length = 2U;
codepoint = value[0] & 0x1fU;
} else if (value[0] >= 0xe0U && value[0] <= 0xefU) {
length = 3U;
codepoint = value[0] & 0x0fU;
} else if (value[0] >= 0xf0U && value[0] <= 0xf4U) {
length = 4U;
codepoint = value[0] & 0x07U;
} else {
return 0;
}
if (remaining < length) return 0;
for (i = 1U; i < length; i++) {
if ((value[i] & 0xc0U) != 0x80U) return 0;
codepoint = (codepoint << 6U) | (value[i] & 0x3fU);
}
if ((length == 3U && codepoint < 0x800U) ||
(length == 4U && codepoint < 0x10000U) ||
codepoint > 0x10ffffU ||
(codepoint >= 0xd800U && codepoint <= 0xdfffU)) return 0;
*character_length = length;
if (decoded != NULL) *decoded = codepoint;
return 1;
}
static int
EmbeddedUnicodeLength(const unsigned char *value, size_t remaining,
size_t *encoded_length)
{
size_t digits = 0U;
unsigned int codepoint = 0U;
if (remaining < 6U || value[0] != '\\' || value[1] != 'x' ||
value[2] != '{') return 0;
while (3U + digits < remaining && digits < 6U &&
isxdigit(value[3U + digits])) {
unsigned char digit = value[3U + digits];
codepoint <<= 4U;
if (digit >= '0' && digit <= '9') codepoint |= digit - '0';
else codepoint |= (unsigned int)(toupper(digit) - 'A' + 10);
digits++;
}
if (digits < 2U || 3U + digits >= remaining ||
value[3U + digits] != '}' || codepoint == 0U ||
codepoint > 0x10ffffU ||
(codepoint >= 0xd800U && codepoint <= 0xdfffU)) return 0;
if (codepoint < 0x80U && codepoint > 0x20U && codepoint != 0x2bU &&
codepoint != 0x3dU && codepoint != 0x5cU && codepoint != 0x7fU)
return 0;
*encoded_length = digits + 4U;
return 1;
}
static int
Utf8OrcptAddressValid(const char *value, size_t length, int smtp_utf8)
{
const unsigned char *cursor = (const unsigned char *)value;
size_t remaining = length;
if (remaining == 0U) return 0;
while (remaining > 0U) {
size_t consumed;
unsigned char current = *cursor;
if (current == '\\') {
if (!EmbeddedUnicodeLength(cursor, remaining, &consumed))
return 0;
} else if (current >= 0x80U) {
if (!smtp_utf8 ||
!Utf8CharacterLength(cursor, remaining, &consumed, NULL))
return 0;
} else {
if (current < 0x21U || current == 0x2bU || current == 0x3dU ||
current == 0x5cU || current > 0x7eU) return 0;
consumed = 1U;
}
cursor += consumed;
remaining -= consumed;
}
return 1;
}
static int
AppendWire(char *result, size_t result_size, size_t *used,
const char *value, size_t length)
{
if (length >= result_size - *used) return 0;
memcpy(result + *used, value, length);
*used += length;
result[*used] = '\0';
return 1;
}
static int
AppendEmbedded(char *result, size_t result_size, size_t *used,
unsigned int codepoint)
{
static const char hex[] = "0123456789ABCDEF";
char encoded[11];
size_t digits = 0U;
size_t hex_digits;
unsigned int value = codepoint;
do {
digits++;
value >>= 4U;
} while (value != 0U);
if (digits < 2U) digits = 2U;
hex_digits = digits;
encoded[0] = '\\';
encoded[1] = 'x';
encoded[2] = '{';
value = codepoint;
while (digits > 0U) {
encoded[2U + digits] = hex[value & 0x0fU];
value >>= 4U;
digits--;
}
encoded[3U + hex_digits] = '}';
return AppendWire(result, result_size, used, encoded, hex_digits + 4U);
}
int
SMTPFormatOrcptValue(const char *original, int smtp_utf8,
char *wire_value, size_t wire_value_size)
{
const char *separator;
const unsigned char *cursor;
const char *address;
size_t type_length;
size_t address_length;
size_t remaining;
size_t used = 0U;
int untyped_utf8 = 0;
if (wire_value != NULL && wire_value_size != 0U) wire_value[0] = '\0';
if (original == NULL || original[0] == '\0' || wire_value == NULL ||
wire_value_size == 0U) return 0;
separator = strchr(original, ';');
if (separator == NULL) {
int has_utf8 = 0;
char encoded[SMTP_ORCPT_MAX * 3U + 1U];
for (cursor = (const unsigned char *)original; *cursor; cursor++) {
if (*cursor >= 0x80U) {
has_utf8 = 1;
break;
}
}
if (!has_utf8) {
int written;
if (!SMTPXtextEncode(original, encoded, sizeof(encoded))) return 0;
written = snprintf(wire_value, wire_value_size, "rfc822;%s",
encoded);
return written > 0 && (size_t)written < wire_value_size;
}
address = original;
address_length = strlen(original);
untyped_utf8 = 1;
} else {
size_t i;
int utf8_type;
type_length = (size_t)(separator - original);
address = separator + 1;
address_length = strlen(address);
if (type_length == 0U || type_length > 64U || address_length == 0U)
return 0;
for (i = 0U; i < type_length; i++) {
unsigned char current = (unsigned char)original[i];
if (!isalnum(current) && current != '-') return 0;
}
utf8_type = type_length == 5U &&
CommandEquals(original, type_length, "UTF-8");
if (!utf8_type) {
if (!XtextValid(address, address_length, SMTP_ORCPT_MAX) ||
type_length + 1U + address_length >= wire_value_size) return 0;
memcpy(wire_value, original, type_length + 1U + address_length + 1U);
return 1;
}
}
if (!untyped_utf8 &&
!Utf8OrcptAddressValid(address, address_length, 1)) return 0;
if (!AppendWire(wire_value, wire_value_size, &used, "utf-8;", 6U))
return 0;
if (smtp_utf8 && !untyped_utf8)
return AppendWire(wire_value, wire_value_size, &used, address,
address_length);
cursor = (const unsigned char *)address;
remaining = address_length;
while (remaining > 0U) {
size_t consumed = 1U;
if (*cursor >= 0x80U) {
unsigned int codepoint;
if (!Utf8CharacterLength(cursor, remaining, &consumed,
&codepoint)) return 0;
if (smtp_utf8) {
if (!AppendWire(wire_value, wire_value_size, &used,
(const char *)cursor, consumed)) return 0;
} else if (!AppendEmbedded(wire_value, wire_value_size, &used,
codepoint)) return 0;
} else if (untyped_utf8 &&
(*cursor <= 0x20U || *cursor == 0x2bU ||
*cursor == 0x3dU || *cursor == 0x5cU ||
*cursor == 0x7fU)) {
if (!AppendEmbedded(wire_value, wire_value_size, &used,
*cursor)) return 0;
} else {
if (!AppendWire(wire_value, wire_value_size, &used,
(const char *)cursor, 1U)) return 0;
}
cursor += consumed;
remaining -= consumed;
}
return 1;
}
int
SMTPFormatDsnMailOptions(int ret_explicit, int ret_full,
const char *envid, char *options,
size_t options_size)
{
size_t used = 0U;
size_t envid_length;
if (options == NULL || options_size == 0U) return 0;
options[0] = '\0';
if (ret_explicit &&
!AppendWire(options, options_size, &used,
ret_full ? " RET=FULL" : " RET=HDRS", 9U))
return 0;
if (envid == NULL || envid[0] == '\0') return 1;
envid_length = strlen(envid);
if (!XtextValid(envid, envid_length, SMTP_ENVID_MAX) ||
!AppendWire(options, options_size, &used, " ENVID=", 7U) ||
!AppendWire(options, options_size, &used, envid, envid_length))
return 0;
return 1;
}
int
SMTPFormatDsnRcptOptions(int notify_explicit,
unsigned int notify,
const char *original_recipient,
int smtp_utf8, char *options,
size_t options_size)
{
char orcpt[SMTP_ORCPT_WIRE_MAX];
size_t used = 0U;
int started = 0;
if (options == NULL || options_size == 0U ||
(notify & ~(SMTP_NOTIFY_SUCCESS | SMTP_NOTIFY_FAILURE |
SMTP_NOTIFY_DELAY)) != 0U)
return 0;
options[0] = '\0';
if (original_recipient != NULL && original_recipient[0] != '\0') {
size_t length;
if (!SMTPFormatOrcptValue(original_recipient, smtp_utf8, orcpt,
sizeof(orcpt)))
return 0;
length = strlen(orcpt);
if (!AppendWire(options, options_size, &used, " ORCPT=", 7U) ||
!AppendWire(options, options_size, &used, orcpt, length))
return 0;
}
if (!notify_explicit) return 1;
if (!AppendWire(options, options_size, &used, " NOTIFY=", 8U))
return 0;
if (notify == 0U)
return AppendWire(options, options_size, &used, "NEVER", 5U);
if (notify & SMTP_NOTIFY_SUCCESS) {
if (!AppendWire(options, options_size, &used, "SUCCESS", 7U))
return 0;
started = 1;
}
if (notify & SMTP_NOTIFY_FAILURE) {
if (started &&
!AppendWire(options, options_size, &used, ",", 1U))
return 0;
if (!AppendWire(options, options_size, &used, "FAILURE", 7U))
return 0;
started = 1;
}
if (notify & SMTP_NOTIFY_DELAY) {
if (started &&
!AppendWire(options, options_size, &used, ",", 1U))
return 0;
if (!AppendWire(options, options_size, &used, "DELAY", 5U))
return 0;
}
return 1;
}
int
SMTPDeliveryResultFromReply(unsigned int reply_code,
const char *reply_text)
{
const char *enhanced = reply_text;
if (reply_code / 100U == 2U) return DELIVER_SUCCESS;
if (reply_code / 100U == 4U) return DELIVER_TRY_LATER;
if (reply_code / 100U != 5U) return DELIVER_TRY_LATER;
if (enhanced == NULL) return DELIVER_FAILURE;
if (isdigit((unsigned char)enhanced[0]) &&
isdigit((unsigned char)enhanced[1]) &&
isdigit((unsigned char)enhanced[2]) &&
(enhanced[3] == ' ' || enhanced[3] == '-'))
enhanced += 4;
while (*enhanced == ' ' || *enhanced == '\t') enhanced++;
if (enhanced[0] != '5' || enhanced[1] != '.' ||
!isdigit((unsigned char)enhanced[2]) || enhanced[3] != '.' ||
!isdigit((unsigned char)enhanced[4]))
return DELIVER_FAILURE;
if (enhanced[2] == '1' && enhanced[4] == '1')
return DELIVER_USER_UNKNOWN;
if (enhanced[2] == '1' && enhanced[4] == '3')
return DELIVER_BOGUS_NAME;
if (enhanced[2] == '2' && enhanced[4] == '2')
return DELIVER_QUOTA_EXCEEDED;
if (enhanced[2] == '4' && enhanced[4] == '1')
return DELIVER_REFUSED;
if (enhanced[2] == '4' && enhanced[4] == '4')
return DELIVER_HOST_UNKNOWN;
if (enhanced[2] == '4' && enhanced[4] == '6')
return DELIVER_HOPCOUNT_EXCEEDED;
if (enhanced[2] == '4' && enhanced[4] == '7')
return DELIVER_TIMEOUT;
if (enhanced[2] == '7') return DELIVER_BLOCKED;
return DELIVER_FAILURE;
}
static int
OrcptValid(const char *value, size_t length, int smtp_utf8)
{
const char *separator = memchr(value, ';', length);
size_t type_length;
size_t address_length;
size_t i;
if (!separator || separator == value) return 0;
type_length = (size_t)(separator - value);
address_length = length - type_length - 1U;
if (!address_length || type_length > 64U) return 0;
for (i = 0; i < type_length; i++) {
unsigned char current = (unsigned char)value[i];
if (!isalnum(current) && current != '-') return 0;
}
if (TokenEquals(value, type_length, "UTF-8"))
return Utf8OrcptAddressValid(separator + 1, address_length,
smtp_utf8);
return XtextValid(separator + 1, address_length, SMTP_ORCPT_MAX);
}
SMTPParameterResult
SMTPParseRcptParameters(const char *parameters, int smtp_utf8,
SMTPRcptParameters *result)
{
const char *cursor = parameters;
const char *token;
const char *value;
size_t length;
size_t value_length;
if (!result) return SMTP_PARAMETER_SYNTAX;
memset(result, 0, sizeof(*result));
while (NextParameter(&cursor, &token, &length)) {
if (TokenPrefix(token, length, "NOTIFY=", &value, &value_length)) {
if (result->notify_supplied ||
!ParseNotify(value, value_length, &result->notify))
return SMTP_PARAMETER_SYNTAX;
result->notify_supplied = 1;
} else if (TokenPrefix(token, length, "ORCPT=", &value,
&value_length)) {
if (result->orcpt_supplied || value_length > SMTP_ORCPT_MAX ||
!OrcptValid(value, value_length, smtp_utf8))
return SMTP_PARAMETER_SYNTAX;
memcpy(result->orcpt, value, value_length);
result->orcpt[value_length] = '\0';
result->orcpt_supplied = 1;
} else {
return SMTP_PARAMETER_UNSUPPORTED;
}
}
return SMTP_PARAMETER_OK;
}
int
SMTPParseReplyLine(const char *line, size_t line_length, unsigned int *code,
int *continuation, const char **text)
{
unsigned int parsed;
if (code) *code = 0U;
if (continuation) *continuation = 0;
if (text) *text = NULL;
if (!line || line_length < 3U || strlen(line) != line_length ||
!isdigit((unsigned char)line[0]) ||
!isdigit((unsigned char)line[1]) ||
!isdigit((unsigned char)line[2])) return 0;
parsed = (unsigned int)(line[0] - '0') * 100U +
(unsigned int)(line[1] - '0') * 10U +
(unsigned int)(line[2] - '0');
if (parsed < 200U || parsed > 599U) return 0;
if (line_length > 3U && line[3] != ' ' && line[3] != '-') return 0;
if (code) *code = parsed;
if (continuation) *continuation = line_length > 3U && line[3] == '-';
if (text) *text = line + (line_length > 3U ? 4U : 3U);
return 1;
}