/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "str.h"
#include "strfuncs.h"
#include "istream.h"
#include "smtp-parser.h"
#include "smtp-reply-parser.h"
#include <ctype.h>
/* From RFC 5321:
Reply-line = *( Reply-code "-" [ textstring ] CRLF )
Reply-code [ SP textstring ] CRLF
Reply-code = %x32-35 %x30-35 %x30-39
textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII
Greeting = ( "220 " (Domain / address-literal)
[ SP textstring ] CRLF ) /
( "220-" (Domain / address-literal)
[ SP textstring ] CRLF
*( "220-" [ textstring ] CRLF )
"220" [ SP textstring ] CRLF )
ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
/ ( "250-" Domain [ SP ehlo-greet ] CRLF
*( "250-" ehlo-line CRLF )
"250" SP ehlo-line CRLF )
ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127)
; string of any characters other than CR or LF
ehlo-line = ehlo-keyword *( SP ehlo-param )
ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
; additional syntax of ehlo-params depends on
; ehlo-keyword
ehlo-param = 1*(%d33-126)
; any CHAR excluding <SP> and all
; control characters (US-ASCII 0-31 and 127
; inclusive)
From RFC 2034:
status-code ::= class "." subject "." detail
class ::= "2" / "4" / "5"
subject ::= 1*3digit
detail ::= 1*3digit
*/
enum smtp_reply_parser_state {
};
struct smtp_reply_parser_state_data {
unsigned int line;
};
struct smtp_reply_parser {
char *error;
};
const char *format, ...)
{
}
struct smtp_reply_parser *
{
return parser;
}
{
}
{
}
}
static void
{
}
static int smtp_reply_parse_code
{
const unsigned char *p;
/* Reply-code = %x32-35 %x30-35 %x30-39
*/
return -1;
return 0;
return -1;
if (p[0] < '2' || p[0] > '5' || p[1] > '5')
return -1;
return 1;
}
{
/* textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII
*/
"Reply exceeds size limit");
return -1;
}
return 0;
return 1;
}
{
/* Domain [ SP ...
*/
"Reply exceeds size limit");
return -1;
}
return 0;
return 1;
}
{
/* ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127)
*
* The greet is not supposed to be empty, but we don't really care
*/
return 0;
for (;;) {
parser->max_reply_size) {
"Reply exceeds size limit");
return -1;
}
/* sanitize bad characters */
return 0;
break;
}
}
return 1;
}
static inline const char *_chr_sanitize(unsigned char c)
{
if (c >= 0x20 && c < 0x7F)
return t_strdup_printf("'%c'", c);
return t_strdup_printf("0x%02x", c);
}
static void
const char **pos)
{
const char *p = *pos;
unsigned int digits, x, y, z;
if (prevx == 9)
return; /* failed on earlier line */
/* status-code ::= class "." subject "." detail
class ::= "2" / "4" / "5"
subject ::= 1*3digit
detail ::= 1*3digit
*/
/* class */
if (p[1] != '.' || (p[0] != '2' && p[0] != '4' && p[0] != '5'))
return;
x = p[0] - '0';
p += 2;
/* subject */
digits = 0;
y = 0;
y = y*10 + (*p - '0');
p++;
}
if (digits == 0 || *p != '.')
return;
p++;
/* detail */
digits = 0;
z = 0;
z = z*10 + (*p - '0');
p++;
}
return;
p++;
/* code is syntactically valid; strip code from textstring */
*pos = p;
/* check for match with status */
/* ignore code */
return;
}
/* check for code consistency */
/* ignore code */
return;
}
}
{
}
}
{
unsigned int status;
int ret;
/*
Reply-line = *( Reply-code "-" [ textstring ] CRLF )
Reply-code [ SP textstring ] CRLF
Reply-code = %x32-35 %x30-35 %x30-39
ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
/ ( "250-" Domain [ SP ehlo-greet ] CRLF
*( "250-" ehlo-line CRLF )
"250" SP ehlo-line CRLF )
*/
for (;;) {
/* fall through */
/* Reply-code */
if (ret < 0) {
"Invalid status code in reply");
}
return ret;
}
"Inconsistent status codes in reply");
return -1;
}
return 0;
/* fall through */
/* "-" / SP / CRLF */
/* "-" [ textstring ] CRLF */
case '-':
break;
/* SP [ textstring ] CRLF ; allow missing text */
case ' ':
break;
/* CRLF */
case '\r':
case '\n':
break;
default:
"Encountered unexpected %s after reply status code",
return -1;
}
break;
/* fall through */
/* textstring / (Domain [ SP ehlo-greet ]) */
/* handle first line of EHLO success response
differently because it can contain control
characters (WHY??!) */
return ret;
return 0;
break;
}
return ret;
return 0;
/* fall through */
/* CR */
} else {
}
return 0;
/* fall through */
/* CRLF / LF */
"Encountered stray CR in reply text");
} else {
"Encountered stray %s in reply text",
}
return -1;
}
return 1;
}
break;
/* SP ehlo-greet */
break;
}
return 0;
/* fall through */
/* ehlo-greet */
return ret;
return 0;
break;
default:
i_unreached();
}
}
i_unreached();
return -1;
}
{
int ret;
return -1;
if (ret > 0)
return 1;
}
if (ret < 0) {
return 0;
"Premature end of input");
} else {
"Stream error: %s",
}
}
return ret;
}
const char **error_r)
{
int ret;
/*
Reply-line = *( Reply-code "-" [ textstring ] CRLF )
Reply-code [ SP textstring ] CRLF
Reply-code = %x32-35 %x30-35 %x30-39
textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII
Greeting is not handled specially here.
*/
return ret;
}
return 1;
}
{
int ret;
/*
ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
/ ( "250-" Domain [ SP ehlo-greet ] CRLF
*( "250-" ehlo-line CRLF )
"250" SP ehlo-line CRLF )
ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127)
; string of any characters other than CR or LF
ehlo-line = ehlo-keyword *( SP ehlo-param )
ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
; additional syntax of ehlo-params depends on
; ehlo-keyword
ehlo-param = 1*(%d33-126)
; any CHAR excluding <SP> and all
; control characters (US-ASCII 0-31 and 127
; inclusive)
*/
return ret;
}
return 1;
}