LCOV - code coverage report
Current view: top level - src - SMTP.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 40 369 10.8 %
Date: 2010-12-13 Functions: 4 22 18.2 %
Branches: 7 274 2.6 %

           Branch data     Line data    Source code
       1                 :            : // $Id: SMTP.cc 6782 2009-06-28 02:19:03Z vern $
       2                 :            : //
       3                 :            : // See the file "COPYING" in the main distribution directory for copyright.
       4                 :            : 
       5                 :            : #include "config.h"
       6                 :            : 
       7                 :            : #include <stdlib.h>
       8                 :            : 
       9                 :            : #include "NetVar.h"
      10                 :            : #include "SMTP.h"
      11                 :            : #include "Event.h"
      12                 :            : #include "ContentLine.h"
      13                 :            : #include "TCP_Rewriter.h"
      14                 :            : 
      15                 :            : #undef SMTP_CMD_DEF
      16                 :            : #define SMTP_CMD_DEF(cmd)       #cmd,
      17                 :            : 
      18                 :            : static const char* smtp_cmd_word[] = {
      19                 :            : #include "SMTP_cmd.def"
      20                 :            : };
      21                 :            : 
      22                 :            : #define SMTP_CMD_WORD(code) ((code >= 0) ? smtp_cmd_word[code] : "(UNKNOWN)")
      23                 :            : 
      24                 :            : 
      25                 :          4 : SMTP_Analyzer::SMTP_Analyzer(Connection* conn)
      26                 :          4 : : TCP_ApplicationAnalyzer(AnalyzerTag::SMTP, conn)
      27                 :            :         {
      28                 :          4 :         expect_sender = 0;
      29                 :          4 :         expect_recver = 1;
      30                 :          4 :         state = SMTP_CONNECTED;
      31                 :          4 :         last_replied_cmd = -1;
      32                 :          4 :         first_cmd = SMTP_CMD_CONN_ESTABLISHMENT;
      33                 :          4 :         pending_reply = 0;
      34                 :            : 
      35                 :            :         // Some clients appear to assume pipelining is always enabled
      36                 :            :         // and do not bother to check whether "PIPELINING" appears in
      37                 :            :         // the server reply to EHLO.
      38                 :          4 :         pipelining = 1;
      39                 :            : 
      40                 :          4 :         skip_data = 0;
      41                 :          4 :         orig_is_sender = true;
      42                 :          4 :         line_after_gap = 0;
      43                 :          4 :         mail = 0;
      44                 :          4 :         UpdateState(first_cmd, 0);
      45                 :          4 :         ContentLine_Analyzer* cl_orig = new ContentLine_Analyzer(conn, true);
      46                 :          4 :         cl_orig->SetIsNULSensitive(true);
      47                 :          4 :         cl_orig->SetSkipPartial(true);
      48                 :          4 :         AddSupportAnalyzer(cl_orig);
      49                 :            : 
      50                 :          4 :         ContentLine_Analyzer* cl_resp = new ContentLine_Analyzer(conn, false);
      51                 :          4 :         cl_resp->SetIsNULSensitive(true);
      52                 :          4 :         cl_resp->SetSkipPartial(true);
      53                 :          4 :         AddSupportAnalyzer(cl_resp);
      54                 :          4 :         }
      55                 :            : 
      56                 :          0 : void SMTP_Analyzer::ConnectionFinished(int half_finished)
      57                 :            :         {
      58                 :          0 :         TCP_ApplicationAnalyzer::ConnectionFinished(half_finished);
      59                 :            : 
      60   [ #  #  #  # ]:          0 :         if ( ! half_finished && mail )
      61                 :          0 :                 EndData();
      62                 :          0 :         }
      63                 :            : 
      64                 :          4 : SMTP_Analyzer::~SMTP_Analyzer()
      65                 :            :         {
      66 [ -  + ][ #  # ]:          4 :         delete line_after_gap;
                 [ #  # ]
      67 [ +  - ][ #  # ]:          4 :         }
                 [ #  # ]
      68                 :            : 
      69                 :          4 : void SMTP_Analyzer::Done()
      70                 :            :         {
      71                 :          4 :         TCP_ApplicationAnalyzer::Done();
      72                 :            : 
      73         [ -  + ]:          4 :         if ( mail )
      74                 :          0 :                 EndData();
      75                 :          4 :         }
      76                 :            : 
      77                 :          0 : void SMTP_Analyzer::Undelivered(int seq, int len, bool is_orig)
      78                 :            :         {
      79                 :          0 :         TCP_ApplicationAnalyzer::Undelivered(seq, len, is_orig);
      80                 :            : 
      81         [ #  # ]:          0 :         if ( len <= 0 )
      82                 :          0 :                 return;
      83                 :            : 
      84                 :          0 :         const char* buf = fmt("seq = %d, len = %d", seq, len);
      85                 :          0 :         int buf_len = strlen(buf);
      86                 :            : 
      87                 :          0 :         Unexpected(is_orig, "content gap", buf_len, buf);
      88                 :            : 
      89         [ #  # ]:          0 :         if ( state == SMTP_IN_DATA )
      90                 :            :                 // Record the SMTP data gap and terminate the
      91                 :            :                 // ongoing mail transaction.
      92                 :          0 :                 EndData();
      93                 :            : 
      94         [ #  # ]:          0 :         if ( line_after_gap )
      95                 :            :                 {
      96         [ #  # ]:          0 :                 delete line_after_gap;
      97                 :          0 :                 line_after_gap = 0;
      98                 :            :                 }
      99                 :            : 
     100                 :          0 :         pending_cmd_q.clear();
     101                 :            : 
     102                 :          0 :         first_cmd = last_replied_cmd = -1;
     103                 :            : 
     104                 :            :         // Missing either the sender's packets or their replies
     105                 :            :         // (e.g. code 354) is critical, so we set state to SMTP_AFTER_GAP
     106                 :            :         // in both cases
     107                 :          0 :         state = SMTP_AFTER_GAP;
     108                 :            :         }
     109                 :            : 
     110                 :          0 : void SMTP_Analyzer::DeliverStream(int length, const u_char* line, bool orig)
     111                 :            :         {
     112                 :          0 :         TCP_ApplicationAnalyzer::DeliverStream(length, line, orig);
     113                 :            : 
     114                 :            :         // NOTE: do not use IsOrig() here, because of TURN command.
     115         [ #  # ]:          0 :         int is_sender = orig_is_sender ? orig : ! orig;
     116                 :            : 
     117                 :            : #if 0
     118                 :            :         ###
     119                 :            :         if ( line[length] != '\r' || line[length+1] != '\n' )
     120                 :            :                 Unexpected(is_sender, "line does not end with <CR><LF>", length, line);
     121                 :            : #endif
     122                 :            : 
     123                 :            :         // Some weird client uses '\r\r\n' for end-of-line sequence
     124                 :            :         // So we make a compromise here to allow /(\r)*\n/ as end-of-line sequences
     125 [ #  # ][ #  # ]:          0 :         if ( length > 0 && line[length-1] == '\r' )
     126                 :            :                 {
     127                 :          0 :                 Unexpected(is_sender, "more than one <CR> at the end of line", length, (const char*) line);
     128 [ #  # ][ #  # ]:          0 :                 do
     129                 :          0 :                         --length;
     130                 :            :                 while ( length > 0 && line[length-1] == '\r' );
     131                 :            :                 }
     132                 :            : 
     133         [ #  # ]:          0 :         for ( int i = 0; i < length; ++i )
     134 [ #  # ][ #  # ]:          0 :                 if ( line[i] == '\r' || line[i] == '\n' )
     135                 :            :                         {
     136                 :            :                         Unexpected(is_sender, "Bare <CR> or <LF> appears in the middle of line",
     137                 :          0 :                                    length, (const char*) line);
     138                 :          0 :                         break;
     139                 :            :                         }
     140                 :            : 
     141                 :          0 :         ProcessLine(length, (const char*) line, orig);
     142                 :          0 :         }
     143                 :            : 
     144                 :            : 
     145                 :          0 : void SMTP_Analyzer::ProcessLine(int length, const char* line, bool orig)
     146                 :            :         {
     147                 :          0 :         const char* end_of_line = line + length;
     148         [ #  # ]:          0 :         if ( state == SMTP_IN_TLS )
     149                 :            :                 // Do not try to parse contents after STARTTLS/220.
     150                 :          0 :                 return;
     151                 :            : 
     152                 :          0 :         int cmd_len = 0;
     153                 :          0 :         const char* cmd = "";
     154                 :            : 
     155                 :            :         // NOTE: do not use IsOrig() here, because of TURN command.
     156         [ #  # ]:          0 :         int is_sender = orig_is_sender ? orig : ! orig;
     157                 :            : 
     158 [ #  # ][ #  # ]:          0 :         if ( ! pipelining &&
         [ #  # ][ #  # ]
                 [ #  # ]
     159                 :            :              ((is_sender && ! expect_sender) ||
     160                 :            :               (! is_sender && ! expect_recver)) )
     161                 :          0 :                 Unexpected(is_sender, "out of order", length, line);
     162                 :            : 
     163         [ #  # ]:          0 :         if ( is_sender )
     164                 :            :                 {
     165                 :          0 :                 int cmd_code = -1;
     166                 :            : 
     167         [ #  # ]:          0 :                 if ( state == SMTP_AFTER_GAP )
     168                 :            :                         {
     169                 :            :                         // Don't know whether it is a command line or
     170                 :            :                         // a data line.
     171         [ #  # ]:          0 :                         if ( line_after_gap )
     172         [ #  # ]:          0 :                                 delete line_after_gap;
     173                 :            : 
     174                 :            :                         line_after_gap =
     175                 :          0 :                                 new BroString((const u_char *) line, length, 1);
     176                 :            :                         }
     177                 :            : 
     178 [ #  # ][ #  # ]:          0 :                 else if ( state == SMTP_IN_DATA && line[0] == '.' && length == 1 )
                 [ #  # ]
     179                 :            :                         {
     180                 :          0 :                         cmd = ".";
     181                 :          0 :                         cmd_len = 1;
     182                 :          0 :                         cmd_code = SMTP_CMD_END_OF_DATA;
     183                 :          0 :                         NewCmd(cmd_code);
     184                 :            : 
     185                 :          0 :                         expect_sender = 0;
     186                 :          0 :                         expect_recver = 1;
     187                 :            :                         }
     188                 :            : 
     189         [ #  # ]:          0 :                 else if ( state == SMTP_IN_DATA )
     190                 :            :                         {
     191                 :            :                         // Check "." for end of data.
     192                 :          0 :                         expect_recver = 0; // ?? MAY server respond to mail data?
     193                 :            : 
     194         [ #  # ]:          0 :                         if ( line[0] == '.' )
     195                 :          0 :                                 ++line;
     196                 :            : 
     197                 :          0 :                         int data_len = end_of_line - line;
     198                 :            : 
     199         [ #  # ]:          0 :                         if ( ! mail )
     200                 :            :                                 // This can happen if we're already shut
     201                 :            :                                 // down the connection due to seeing a RST
     202                 :            :                                 // but are now processing packets sent
     203                 :            :                                 // afterwards (because, e.g., the RST was
     204                 :            :                                 // dropped or ignored).
     205                 :          0 :                                 BeginData();
     206                 :            : 
     207                 :          0 :                         ProcessData(data_len, line);
     208                 :            : 
     209   [ #  #  #  # ]:          0 :                         if ( smtp_data && ! skip_data )
                 [ #  # ]
     210                 :            :                                 {
     211                 :          0 :                                 val_list* vl = new val_list;
     212                 :          0 :                                 vl->append(BuildConnVal());
     213                 :          0 :                                 vl->append(new Val(orig, TYPE_BOOL));
     214                 :          0 :                                 vl->append(new StringVal(data_len, line));
     215                 :          0 :                                 ConnectionEvent(smtp_data, vl);
     216                 :            :                                 }
     217                 :            :                         }
     218                 :            : 
     219         [ #  # ]:          0 :                 else if ( state == SMTP_IN_AUTH )
     220                 :            :                         {
     221                 :          0 :                         cmd = "***";
     222                 :          0 :                         cmd_len = 2;
     223                 :          0 :                         cmd_code = SMTP_CMD_AUTH_ANSWER;
     224                 :          0 :                         NewCmd(cmd_code);
     225                 :            :                         }
     226                 :            : 
     227                 :            :                 else
     228                 :            :                         {
     229                 :          0 :                         expect_sender = 0;
     230                 :          0 :                         expect_recver = 1;
     231                 :            : 
     232                 :          0 :                         get_word(length, line, cmd_len, cmd);
     233                 :          0 :                         line = skip_whitespace(line + cmd_len, end_of_line);
     234                 :          0 :                         cmd_code = ParseCmd(cmd_len, cmd);
     235                 :            : 
     236         [ #  # ]:          0 :                         if ( cmd_code == -1 )
     237                 :            :                                 {
     238                 :          0 :                                 Unexpected(1, "unknown command", cmd_len, cmd);
     239                 :          0 :                                 cmd = 0;
     240                 :            :                                 }
     241                 :            :                         else
     242                 :          0 :                                 NewCmd(cmd_code);
     243                 :            :                         }
     244                 :            : 
     245                 :            :                 // Generate smtp_request event
     246         [ #  # ]:          0 :                 if ( cmd_code >= 0 )
     247                 :            :                         {
     248                 :            :                         // In order for all MIME events nested
     249                 :            :                         // between SMTP command DATA and END_OF_DATA,
     250                 :            :                         // we need to call UpdateState(), which in
     251                 :            :                         // turn calls BeginData() and EndData(),  and
     252                 :            :                         // RequestEvent() in different orders for the
     253                 :            :                         // two commands.
     254         [ #  # ]:          0 :                         if ( cmd_code == SMTP_CMD_END_OF_DATA )
     255                 :          0 :                                 UpdateState(cmd_code, 0);
     256                 :            : 
     257         [ #  # ]:          0 :                         if ( smtp_request )
     258                 :            :                                 {
     259                 :          0 :                                 int data_len = end_of_line - line;
     260                 :          0 :                                 RequestEvent(cmd_len, cmd, data_len, line);
     261                 :            :                                 }
     262                 :            : 
     263         [ #  # ]:          0 :                         if ( cmd_code != SMTP_CMD_END_OF_DATA )
     264                 :          0 :                                 UpdateState(cmd_code, 0);
     265                 :            :                         }
     266                 :            :                 }
     267                 :            : 
     268                 :            :         else
     269                 :            :                 {
     270                 :            :                 int reply_code;
     271                 :            : 
     272 [ #  # ][ #  # ]:          0 :                 if ( length >= 3 &&
         [ #  # ][ #  # ]
     273                 :            :                      isdigit(line[0]) && isdigit(line[1]) && isdigit(line[2]) )
     274                 :            :                         {
     275                 :            :                         reply_code = (line[0] - '0') * 100 +
     276                 :            :                                         (line[1] - '0') * 10 +
     277                 :          0 :                                         (line[2] - '0');
     278                 :            :                         }
     279                 :            :                 else
     280                 :          0 :                         reply_code = -1;
     281                 :            : 
     282                 :            :                 // The first digit of reply code must be between 1 and 5,
     283                 :            :                 // and the second between 0 and 5 (RFC 2821).  But sometimes
     284                 :            :                 // we see 5xx codes larger than 559, so we still tolerate that.
     285 [ #  # ][ #  # ]:          0 :                 if ( reply_code < 100 || reply_code > 599 )
     286                 :            :                         {
     287                 :          0 :                         reply_code = -1;
     288                 :          0 :                         Unexpected(is_sender, "reply code out of range", length, line);
     289                 :            :                         ProtocolViolation(fmt("reply code %d out of range",
     290                 :          0 :                                                 reply_code), line, length);
     291                 :            :                         }
     292                 :            : 
     293                 :            :                 else
     294                 :            :                         { // Valid reply code.
     295 [ #  # ][ #  # ]:          0 :                         if ( pending_reply && reply_code != pending_reply )
     296                 :            :                                 {
     297                 :          0 :                                 Unexpected(is_sender, "reply code does not match the continuing reply", length, line);
     298                 :          0 :                                 pending_reply = 0;
     299                 :            :                                 }
     300                 :            : 
     301 [ #  # ][ #  # ]:          0 :                         if ( ! pending_reply && reply_code >= 0 )
     302                 :            :                                 // It is not a continuation.
     303                 :          0 :                                 NewReply(reply_code);
     304                 :            : 
     305                 :            :                         // Update pending_reply.
     306 [ #  # ][ #  # ]:          0 :                         if ( reply_code >= 0 && length > 3 && line[3] == '-' )
                 [ #  # ]
     307                 :            :                                 { // A continued reply.
     308                 :          0 :                                 pending_reply = reply_code;
     309                 :          0 :                                 line = skip_whitespace(line+4, end_of_line);
     310                 :            :                                 }
     311                 :            : 
     312                 :            :                         else
     313                 :            :                                 { // This is the end of the reply.
     314                 :          0 :                                 line = skip_whitespace(line+3, end_of_line);
     315                 :            : 
     316                 :          0 :                                 pending_reply = 0;
     317                 :          0 :                                 expect_sender = 1;
     318                 :          0 :                                 expect_recver = 0;
     319                 :            :                                 }
     320                 :            : 
     321                 :            :                         // Generate events.
     322 [ #  # ][ #  # ]:          0 :                         if ( smtp_reply && reply_code >= 0 )
                 [ #  # ]
     323                 :            :                                 {
     324                 :          0 :                                 const int cmd_code = last_replied_cmd;
     325      [ #  #  # ]:          0 :                                 switch ( cmd_code ) {
     326                 :            :                                         case SMTP_CMD_CONN_ESTABLISHMENT:
     327                 :          0 :                                                 cmd = ">";
     328                 :          0 :                                                 break;
     329                 :            : 
     330                 :            :                                         case SMTP_CMD_END_OF_DATA:
     331                 :          0 :                                                 cmd = ".";
     332                 :          0 :                                                 break;
     333                 :            : 
     334                 :            :                                         default:
     335         [ #  # ]:          0 :                                                 cmd = SMTP_CMD_WORD(cmd_code);
     336                 :            :                                                 break;
     337                 :            :                                 }
     338                 :            : 
     339                 :          0 :                                 val_list* vl = new val_list;
     340                 :          0 :                                 vl->append(BuildConnVal());
     341                 :          0 :                                 vl->append(new Val(orig, TYPE_BOOL));
     342                 :          0 :                                 vl->append(new Val(reply_code, TYPE_COUNT));
     343                 :          0 :                                 vl->append(new StringVal(cmd));
     344                 :          0 :                                 vl->append(new StringVal(end_of_line - line, line));
     345                 :          0 :                                 vl->append(new Val((pending_reply > 0), TYPE_BOOL));
     346                 :            : 
     347                 :          0 :                                 ConnectionEvent(smtp_reply, vl);
     348                 :            :                                 }
     349                 :            :                         }
     350                 :            : 
     351                 :            :                 // Process SMTP extensions, e.g. PIPELINING.
     352 [ #  # ][ #  # ]:          0 :                 if ( last_replied_cmd == SMTP_CMD_EHLO && reply_code == 250 )
     353                 :            :                         {
     354                 :            :                         const char* ext;
     355                 :            :                         int ext_len;
     356                 :            : 
     357                 :          0 :                         get_word(end_of_line - line, line, ext_len, ext);
     358                 :          0 :                         line = skip_whitespace(line + ext_len, end_of_line);
     359                 :          0 :                         ProcessExtension(ext_len, ext);
     360                 :            :                         }
     361                 :            :                 }
     362                 :            :         }
     363                 :            : 
     364                 :          0 : void SMTP_Analyzer::NewCmd(const int cmd_code)
     365                 :            :         {
     366         [ #  # ]:          0 :         if ( pipelining )
     367                 :            :                 {
     368         [ #  # ]:          0 :                 if ( first_cmd < 0 )
     369                 :          0 :                         first_cmd = cmd_code;
     370                 :            :                 else
     371                 :          0 :                         pending_cmd_q.push_back(cmd_code);
     372                 :            :                 }
     373                 :            :         else
     374                 :          0 :                 first_cmd = cmd_code;
     375                 :          0 :         }
     376                 :            : 
     377                 :            : 
     378                 :            : // Here we keep a SMTP state machine and update it on each reply.
     379                 :            : // However, the purpose is NOT to check correctness of SMTP commands
     380                 :            : // and replies, but to guess the state of the SMTP session and,
     381                 :            : // particularly, to know when we are in the SMTP_IN_DATA state.
     382                 :            : //
     383                 :            : // That is why state transition does not depend on the previous state,
     384                 :            : // but only depend on the <command, reply> pair.
     385                 :            : //
     386                 :            : // Why not simply have two-state machine, IN_DATA/NOT_IN_DATA?  Because
     387                 :            : // we want to understand the behavior of SMTP and check how far it may
     388                 :            : // deviate from our knowledge.
     389                 :            : 
     390                 :          0 : void SMTP_Analyzer::NewReply(const int reply_code)
     391                 :            :         {
     392 [ #  # ][ #  # ]:          0 :         if ( state == SMTP_AFTER_GAP && reply_code > 0 )
     393                 :            :                 {
     394                 :          0 :                 state = SMTP_GAP_RECOVERY;
     395                 :          0 :                 const char* unknown_cmd = SMTP_CMD_WORD(-1);
     396                 :          0 :                 RequestEvent(strlen(unknown_cmd), unknown_cmd, 0, "");
     397                 :            :                 /*
     398                 :            :                 if ( line_after_gap )
     399                 :            :                         ProcessLine(sender, line_after_gap->Len(), (const char *) line_after_gap->Bytes());
     400                 :            :                 */
     401                 :            :                 }
     402                 :            : 
     403                 :            :         // Make all parameters constants.
     404                 :          0 :         const int cmd_code = first_cmd;
     405                 :            : 
     406                 :            :         // To recover from a gap, we detect replies -- the critical
     407                 :            :         // assumptions here are 1) receiver does not reply during a DATA
     408                 :            :         // session; 2) there is no TURN in the gap.
     409                 :            : 
     410                 :          0 :         last_replied_cmd = first_cmd;
     411                 :          0 :         first_cmd = -1;
     412                 :            : 
     413 [ #  # ][ #  # ]:          0 :         if ( pipelining && pending_cmd_q.size() > 0 )
                 [ #  # ]
     414                 :            :                 {
     415                 :          0 :                 first_cmd = pending_cmd_q.front();
     416                 :          0 :                 pending_cmd_q.pop_front();
     417                 :            :                 }
     418                 :            : 
     419                 :          0 :         UpdateState(cmd_code, reply_code);
     420                 :          0 :         }
     421                 :            : 
     422                 :            : // Note: reply_code == 0 means we haven't seen the reply, in which case we
     423                 :            : // still update the state as if the command will succeed, and later
     424                 :            : // adjust the state if it turns out otherwise. This is because some
     425                 :            : // clients are really aggressive in pipelining (beyond the restrictions
     426                 :            : // in the RPC), and as a result we have to update the state following
     427                 :            : // the commands in addition to the replies.
     428                 :            : 
     429                 :          4 : void SMTP_Analyzer::UpdateState(const int cmd_code, const int reply_code)
     430                 :            :         {
     431                 :          4 :         const int st = state;
     432                 :            : 
     433 [ -  + ][ #  # ]:          4 :         if ( st == SMTP_QUIT && reply_code == 0 )
     434                 :          0 :                 UnexpectedCommand(cmd_code, reply_code);
     435                 :            : 
     436 [ +  -  -  -  - :          4 :         switch ( cmd_code ) {
          -  -  -  -  -  
                -  -  - ]
     437                 :            :         case SMTP_CMD_CONN_ESTABLISHMENT:
     438   [ +  -  -  - ]:          4 :                 switch ( reply_code ) {
     439                 :            :                 case 0:
     440         [ -  + ]:          4 :                         if ( st != SMTP_CONNECTED )
     441                 :            :                                 {
     442                 :            :                                 // Impossible state, because the command
     443                 :            :                                 // CONN_ESTABLISHMENT should only appear
     444                 :            :                                 // in the very beginning.
     445                 :          0 :                                 UnexpectedCommand(cmd_code, reply_code);
     446                 :            :                                 }
     447                 :          4 :                         state = SMTP_INITIATED;
     448                 :          4 :                         break;
     449                 :            : 
     450                 :            :                 case 220:
     451                 :          0 :                         break;
     452                 :            : 
     453                 :            :                 case 421:
     454                 :            :                 case 554:
     455                 :          0 :                         state = SMTP_NOT_AVAILABLE;
     456                 :          0 :                         break;
     457                 :            : 
     458                 :            :                 default:
     459                 :          0 :                         UnexpectedReply(cmd_code, reply_code);
     460                 :            :                         break;
     461                 :            :                 }
     462                 :          4 :                 break;
     463                 :            : 
     464                 :            :         case SMTP_CMD_EHLO:
     465                 :            :         case SMTP_CMD_HELO:
     466   [ #  #  #  # ]:          0 :                 switch ( reply_code ) {
     467                 :            :                         case 0:
     468         [ #  # ]:          0 :                                 if ( st != SMTP_INITIATED )
     469                 :          0 :                                         UnexpectedCommand(cmd_code, reply_code);
     470                 :          0 :                                 state = SMTP_READY;
     471                 :          0 :                                 break;
     472                 :            : 
     473                 :            :                         case 250:
     474                 :          0 :                                 break;
     475                 :            : 
     476                 :            :                         case 421:
     477                 :            :                         case 500:
     478                 :            :                         case 501:
     479                 :            :                         case 504:
     480                 :            :                         case 550:
     481                 :          0 :                                 state = SMTP_INITIATED;
     482                 :          0 :                                 break;
     483                 :            : 
     484                 :            :                         default:
     485                 :          0 :                                 UnexpectedReply(cmd_code, reply_code);
     486                 :            :                                 break;
     487                 :            :                 }
     488                 :          0 :                 break;
     489                 :            : 
     490                 :            :         case SMTP_CMD_MAIL:
     491                 :            :         case SMTP_CMD_SEND:
     492                 :            :         case SMTP_CMD_SOML:
     493                 :            :         case SMTP_CMD_SAML:
     494   [ #  #  #  # ]:          0 :                 switch ( reply_code ) {
     495                 :            :                         case 0:
     496         [ #  # ]:          0 :                                 if ( st != SMTP_READY )
     497                 :          0 :                                         UnexpectedCommand(cmd_code, reply_code);
     498                 :          0 :                                 state = SMTP_MAIL_OK;
     499                 :          0 :                                 break;
     500                 :            : 
     501                 :            :                         case 250:
     502                 :          0 :                                 break;
     503                 :            : 
     504                 :            :                         case 421:
     505                 :            :                         case 451:
     506                 :            :                         case 452:
     507                 :            :                         case 500:
     508                 :            :                         case 501:
     509                 :            :                         case 503:
     510                 :            :                         case 550:
     511                 :            :                         case 552:
     512                 :            :                         case 553:
     513         [ #  # ]:          0 :                                 if ( state != SMTP_IN_DATA )
     514                 :          0 :                                         state = SMTP_READY;
     515                 :          0 :                                 break;
     516                 :            : 
     517                 :            :                         default:
     518                 :          0 :                                 UnexpectedReply(cmd_code, reply_code);
     519                 :            :                                 break;
     520                 :            :                 }
     521                 :          0 :                 break;
     522                 :            : 
     523                 :            :         case SMTP_CMD_RCPT:
     524   [ #  #  #  # ]:          0 :                 switch ( reply_code ) {
     525                 :            :                         case 0:
     526 [ #  # ][ #  # ]:          0 :                                 if ( st != SMTP_MAIL_OK && st != SMTP_RCPT_OK )
     527                 :          0 :                                         UnexpectedCommand(cmd_code, reply_code);
     528                 :          0 :                                 state = SMTP_RCPT_OK;
     529                 :          0 :                                 break;
     530                 :            : 
     531                 :            :                         case 250:
     532                 :            :                         case 251: // ?? Shall we catch 251? (RFC 2821)
     533                 :          0 :                                 break;
     534                 :            : 
     535                 :            :                         case 421:
     536                 :            :                         case 450:
     537                 :            :                         case 451:
     538                 :            :                         case 452:
     539                 :            :                         case 500:
     540                 :            :                         case 501:
     541                 :            :                         case 503:
     542                 :            :                         case 550:
     543                 :            :                         case 551: // ?? Shall we catch 551?
     544                 :            :                         case 552:
     545                 :            :                         case 553:
     546                 :            :                         case 554: // = transaction failed/recipient refused
     547                 :          0 :                                 break;
     548                 :            : 
     549                 :            :                         default:
     550                 :          0 :                                 UnexpectedReply(cmd_code, reply_code);
     551                 :            :                                 break;
     552                 :            :                 }
     553                 :          0 :                 break;
     554                 :            : 
     555                 :            :         case SMTP_CMD_DATA:
     556   [ #  #  #  #  :          0 :                 switch ( reply_code ) {
                      # ]
     557                 :            :                         case 0:
     558         [ #  # ]:          0 :                                 if ( state != SMTP_RCPT_OK )
     559                 :          0 :                                         UnexpectedCommand(cmd_code, reply_code);
     560                 :          0 :                                 BeginData();
     561                 :          0 :                                 break;
     562                 :            : 
     563                 :            :                         case 354:
     564                 :          0 :                                 break;
     565                 :            : 
     566                 :            :                         case 421:
     567         [ #  # ]:          0 :                                 if ( state == SMTP_IN_DATA )
     568                 :          0 :                                         EndData();
     569                 :          0 :                                 state = SMTP_QUIT;
     570                 :          0 :                                 break;
     571                 :            : 
     572                 :            :                         case 500:
     573                 :            :                         case 501:
     574                 :            :                         case 503:
     575                 :            :                         case 451:
     576                 :            :                         case 554:
     577         [ #  # ]:          0 :                                 if ( state == SMTP_IN_DATA )
     578                 :          0 :                                         EndData();
     579                 :          0 :                                 state = SMTP_READY;
     580                 :          0 :                                 break;
     581                 :            : 
     582                 :            :                         default:
     583                 :          0 :                                 UnexpectedReply(cmd_code, reply_code);
     584         [ #  # ]:          0 :                                 if ( state == SMTP_IN_DATA )
     585                 :          0 :                                         EndData();
     586                 :          0 :                                 state = SMTP_READY;
     587                 :            :                                 break;
     588                 :            :                 }
     589                 :          0 :                 break;
     590                 :            : 
     591                 :            :         case SMTP_CMD_END_OF_DATA:
     592   [ #  #  #  # ]:          0 :                 switch ( reply_code ) {
     593                 :            :                         case 0:
     594         [ #  # ]:          0 :                                 if ( st != SMTP_IN_DATA )
     595                 :          0 :                                         UnexpectedCommand(cmd_code, reply_code);
     596                 :          0 :                                         EndData();
     597                 :          0 :                                 state = SMTP_AFTER_DATA;
     598                 :          0 :                                 break;
     599                 :            : 
     600                 :            :                         case 250:
     601                 :          0 :                                 break;
     602                 :            : 
     603                 :            :                         case 421:
     604                 :            :                         case 451:
     605                 :            :                         case 452:
     606                 :            :                         case 552:
     607                 :            :                         case 554:
     608                 :          0 :                                 break;
     609                 :            : 
     610                 :            :                         default:
     611                 :          0 :                                 UnexpectedReply(cmd_code, reply_code);
     612                 :            :                                 break;
     613                 :            :                 }
     614                 :            : 
     615         [ #  # ]:          0 :                 if ( reply_code > 0 )
     616                 :          0 :                         state = SMTP_READY;
     617                 :          0 :                 break;
     618                 :            : 
     619                 :            :         case SMTP_CMD_RSET:
     620      [ #  #  # ]:          0 :                 switch ( reply_code ) {
     621                 :            :                         case 0:
     622                 :          0 :                                 state = SMTP_READY;
     623                 :          0 :                                 break;
     624                 :            : 
     625                 :            :                         case 250:
     626                 :          0 :                                 break;
     627                 :            : 
     628                 :            :                         default:
     629                 :          0 :                                 UnexpectedReply(cmd_code, reply_code);
     630                 :            :                                 break;
     631                 :            :                 }
     632                 :            : 
     633                 :          0 :                 break;
     634                 :            : 
     635                 :            :         case SMTP_CMD_QUIT:
     636      [ #  #  # ]:          0 :                 switch ( reply_code ) {
     637                 :            :                         case 0:
     638                 :          0 :                                 state = SMTP_QUIT;
     639                 :          0 :                                 break;
     640                 :            : 
     641                 :            :                         case 221:
     642                 :          0 :                                 break;
     643                 :            : 
     644                 :            :                         default:
     645                 :          0 :                                 UnexpectedReply(cmd_code, reply_code);
     646                 :            :                                 break;
     647                 :            :                 }
     648                 :            : 
     649                 :          0 :                 break;
     650                 :            : 
     651                 :            :         case SMTP_CMD_AUTH:
     652         [ #  # ]:          0 :                 if ( st != SMTP_READY )
     653                 :          0 :                         UnexpectedCommand(cmd_code, reply_code);
     654                 :            : 
     655   [ #  #  #  # ]:          0 :                 switch ( reply_code ) {
     656                 :            :                         case 0:
     657                 :            :                                 // Here we wait till there's a reply.
     658                 :          0 :                                 break;
     659                 :            : 
     660                 :            :                         case 334:
     661                 :          0 :                                 state = SMTP_IN_AUTH;
     662                 :          0 :                                 break;
     663                 :            : 
     664                 :            :                         case 235:
     665                 :          0 :                                 state = SMTP_INITIATED;
     666                 :          0 :                                 break;
     667                 :            : 
     668                 :            :                         case 432:
     669                 :            :                         case 454:
     670                 :            :                         case 501:
     671                 :            :                         case 503:
     672                 :            :                         case 504:
     673                 :            :                         case 534:
     674                 :            :                         case 535:
     675                 :            :                         case 538:
     676                 :            :                         default:
     677                 :          0 :                                 state = SMTP_INITIATED;
     678                 :            :                                 break;
     679                 :            :                 }
     680                 :          0 :                 break;
     681                 :            : 
     682                 :            :         case SMTP_CMD_AUTH_ANSWER:
     683         [ #  # ]:          0 :                 if ( st != SMTP_IN_AUTH )
     684                 :          0 :                         UnexpectedCommand(cmd_code, reply_code);
     685                 :            : 
     686      [ #  #  # ]:          0 :                 switch ( reply_code ) {
     687                 :            :                         case 0:
     688                 :            :                                 // Here we wait till there's a reply.
     689                 :          0 :                                 break;
     690                 :            : 
     691                 :            :                         case 334:
     692                 :          0 :                                 state = SMTP_IN_AUTH;
     693                 :          0 :                                 break;
     694                 :            : 
     695                 :            :                         case 235:
     696                 :            :                         case 535:
     697                 :            :                         default:
     698                 :          0 :                                 state = SMTP_INITIATED;
     699                 :            :                                 break;
     700                 :            :                 }
     701                 :          0 :                 break;
     702                 :            : 
     703                 :            :         case SMTP_CMD_TURN:
     704         [ #  # ]:          0 :                 if ( st != SMTP_READY )
     705                 :          0 :                         UnexpectedCommand(cmd_code, reply_code);
     706                 :            : 
     707      [ #  #  # ]:          0 :                 switch ( reply_code ) {
     708                 :            :                         case 0:
     709                 :            :                                 // Here we wait till there's a reply.
     710                 :          0 :                                 break;
     711                 :            : 
     712                 :            :                         case 250:
     713                 :            :                                 // flip-side
     714                 :          0 :                                 orig_is_sender = ! orig_is_sender;
     715                 :            : 
     716                 :          0 :                                 state = SMTP_CONNECTED;
     717                 :          0 :                                 expect_sender = 0;
     718                 :          0 :                                 expect_recver = 1;
     719                 :            :                                 break;
     720                 :            : 
     721                 :            :                         case 502:
     722                 :            :                         default:
     723                 :            :                                 break;
     724                 :            :                 }
     725                 :          0 :                 break;
     726                 :            : 
     727                 :            :         case SMTP_CMD_STARTTLS:
     728         [ #  # ]:          0 :                 if ( st != SMTP_READY )
     729                 :          0 :                         UnexpectedCommand(cmd_code, reply_code);
     730                 :            : 
     731      [ #  #  # ]:          0 :                 switch ( reply_code ) {
     732                 :            :                         case 0:
     733                 :            :                                 // Here we wait till there's a reply.
     734                 :          0 :                                 break;
     735                 :            : 
     736                 :            :                         case 220:
     737                 :          0 :                                 state = SMTP_IN_TLS;
     738                 :          0 :                                 expect_sender = expect_recver = 1;
     739                 :            :                                 break;
     740                 :            : 
     741                 :            :                         case 454:
     742                 :            :                         case 501:
     743                 :            :                         default:
     744                 :            :                                 break;
     745                 :            :                 }
     746                 :          0 :                 break;
     747                 :            : 
     748                 :            :         case SMTP_CMD_VRFY:
     749                 :            :         case SMTP_CMD_EXPN:
     750                 :            :         case SMTP_CMD_HELP:
     751                 :            :         case SMTP_CMD_NOOP:
     752                 :            :                 // These commands do not affect state.
     753                 :            :                 // ?? However, later we may want to add reply
     754                 :            :                 // and state check code.
     755                 :            : 
     756                 :            :         default:
     757 [ #  # ][ #  # ]:          0 :                 if ( st == SMTP_GAP_RECOVERY && reply_code == 354 )
     758                 :            :                         {
     759                 :          0 :                         BeginData();
     760                 :            :                         }
     761                 :            :                 break;
     762                 :            :         }
     763                 :            : 
     764                 :            :         // A hack: whenever the server makes a valid reply during a DATA
     765                 :            :         // section, we assume that the DATA section has ended (the end
     766                 :            :         // of data line might have been lost due to gaps in trace).  Note,
     767                 :            :         // BeginData() won't be called till the next DATA command.
     768                 :            : #if 0
     769                 :            :         if ( state == SMTP_IN_DATA && reply_code >= 400 )
     770                 :            :                 {
     771                 :            :                 EndData();
     772                 :            :                 state = SMTP_READY;
     773                 :            :                 }
     774                 :            : #endif
     775                 :          4 :         }
     776                 :            : 
     777                 :          0 : void SMTP_Analyzer::ProcessExtension(int ext_len, const char* ext)
     778                 :            :         {
     779         [ #  # ]:          0 :         if ( ! ext )
     780                 :          0 :                 return;
     781                 :            : 
     782         [ #  # ]:          0 :         if ( ! strcasecmp_n(ext_len, ext, "PIPELINING") )
     783                 :          0 :                 pipelining = 1;
     784                 :            :         }
     785                 :            : 
     786                 :          0 : int SMTP_Analyzer::ParseCmd(int cmd_len, const char* cmd)
     787                 :            :         {
     788         [ #  # ]:          0 :         if ( ! cmd )
     789                 :          0 :                 return -1;
     790                 :            : 
     791         [ #  # ]:          0 :         for ( int code = SMTP_CMD_EHLO; code < SMTP_CMD_LAST; ++code )
     792         [ #  # ]:          0 :                 if ( ! strcasecmp_n(cmd_len, cmd, smtp_cmd_word[code - SMTP_CMD_EHLO]) )
     793                 :          0 :                         return code;
     794                 :            : 
     795                 :          0 :         return -1;
     796                 :            :         }
     797                 :            : 
     798                 :            : void SMTP_Analyzer::RequestEvent(int cmd_len, const char* cmd,
     799                 :          0 :                                 int arg_len, const char* arg)
     800                 :            :         {
     801                 :          0 :         ProtocolConfirmation();
     802                 :          0 :         val_list* vl = new val_list;
     803                 :            : 
     804                 :          0 :         vl->append(BuildConnVal());
     805                 :          0 :         vl->append(new Val(orig_is_sender, TYPE_BOOL));
     806                 :          0 :         vl->append((new StringVal(cmd_len, cmd))->ToUpper());
     807                 :          0 :         vl->append(new StringVal(arg_len, arg));
     808                 :            : 
     809                 :          0 :         ConnectionEvent(smtp_request, vl);
     810                 :          0 :         }
     811                 :            : 
     812                 :            : void SMTP_Analyzer::Unexpected(const int is_sender, const char* msg,
     813                 :          0 :                                 int detail_len, const char* detail)
     814                 :            :         {
     815                 :            :         // Either party can send a line after an unexpected line.
     816                 :          0 :         expect_sender = expect_recver = 1;
     817                 :            : 
     818         [ #  # ]:          0 :         if ( smtp_unexpected )
     819                 :            :                 {
     820                 :          0 :                 val_list* vl = new val_list;
     821                 :          0 :                 int is_orig = is_sender;
     822         [ #  # ]:          0 :                 if ( ! orig_is_sender )
     823                 :          0 :                         is_orig = ! is_orig;
     824                 :            : 
     825                 :          0 :                 vl->append(BuildConnVal());
     826                 :          0 :                 vl->append(new Val(is_orig, TYPE_BOOL));
     827                 :          0 :                 vl->append(new StringVal(msg));
     828                 :          0 :                 vl->append(new StringVal(detail_len, detail));
     829                 :            : 
     830                 :          0 :                 ConnectionEvent(smtp_unexpected, vl);
     831                 :            :                 }
     832                 :          0 :         }
     833                 :            : 
     834                 :          0 : void SMTP_Analyzer::UnexpectedCommand(const int cmd_code, const int reply_code)
     835                 :            :         {
     836                 :            :         // If this happens, please fix the SMTP state machine!
     837                 :            :         // ### Eventually, these should be turned into "weird" events.
     838                 :            :         static char buf[512];
     839                 :            :         int len = safe_snprintf(buf, sizeof(buf),
     840                 :            :                                 "%s reply = %d state = %d",
     841         [ #  # ]:          0 :                                 SMTP_CMD_WORD(cmd_code), reply_code, state);
     842         [ #  # ]:          0 :         if ( len > (int) sizeof(buf) )
     843                 :          0 :                 len = sizeof(buf);
     844                 :          0 :         Unexpected (1, "unexpected command", len, buf);
     845                 :          0 :         }
     846                 :            : 
     847                 :          0 : void SMTP_Analyzer::UnexpectedReply(const int cmd_code, const int reply_code)
     848                 :            :         {
     849                 :            :         // If this happens, please fix the SMTP state machine!
     850                 :            :         // ### Eventually, these should be turned into "weird" events.
     851                 :            :         static char buf[512];
     852                 :            :         int len = safe_snprintf(buf, sizeof(buf),
     853                 :            :                                 "%d state = %d, last command = %s",
     854         [ #  # ]:          0 :                                 reply_code, state, SMTP_CMD_WORD(cmd_code));
     855                 :          0 :         Unexpected (1, "unexpected reply", len, buf);
     856                 :          0 :         }
     857                 :            : 
     858                 :          0 : void SMTP_Analyzer::ProcessData(int length, const char* line)
     859                 :            :         {
     860                 :          0 :         mail->Deliver(length, line, 1 /* trailing_CRLF */);
     861                 :          0 :         }
     862                 :            : 
     863                 :          0 : void SMTP_Analyzer::BeginData()
     864                 :            :         {
     865                 :          0 :         state = SMTP_IN_DATA;
     866                 :          0 :         skip_data = 0; // reset the flag at the beginning of the mail
     867         [ #  # ]:          0 :         if ( mail != 0 )
     868                 :            :                 {
     869                 :          0 :                 warn("nested mail transaction");
     870                 :          0 :                 mail->Done();
     871         [ #  # ]:          0 :                 delete mail;
     872                 :            :                 }
     873                 :            : 
     874                 :          0 :         mail = new MIME_Mail(this);
     875                 :          0 :         }
     876                 :            : 
     877                 :          0 : void SMTP_Analyzer::EndData()
     878                 :            :         {
     879         [ #  # ]:          0 :         if ( ! mail )
     880                 :          0 :                 warn("Unmatched end of data");
     881                 :            :         else
     882                 :            :                 {
     883                 :          0 :                 mail->Done();
     884         [ #  # ]:          0 :                 delete mail;
     885                 :          0 :                 mail = 0;
     886                 :            :                 }
     887                 :          0 :         }
     888                 :            : 
     889                 :            : #include "smtp-rw.bif.func_def"

Generated by: LCOV version 1.8