"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/dynamic-preprocessors/appid/detector_plugins/detector_smtp.c" (16 Oct 2020, 40120 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "detector_smtp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.16.1_vs_2.9.17.

    1 /*
    2 ** Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
    3 ** Copyright (C) 2005-2013 Sourcefire, Inc.
    4 **
    5 ** This program is free software; you can redistribute it and/or modify
    6 ** it under the terms of the GNU General Public License Version 2 as
    7 ** published by the Free Software Foundation.  You may not use, modify or
    8 ** distribute this program under any other version of the GNU General
    9 ** Public License.
   10 **
   11 ** This program is distributed in the hope that it will be useful,
   12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 ** GNU General Public License for more details.
   15 **
   16 ** You should have received a copy of the GNU General Public License
   17 ** along with this program; if not, write to the Free Software
   18 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   19 */
   20 
   21 
   22 #include <ctype.h>
   23 #include <string.h>
   24 #include <stdlib.h>
   25 #include <stddef.h>
   26 #include <sys/types.h>
   27 #include <netinet/in.h>
   28 #include "appIdApi.h"
   29 #include "appInfoTable.h"
   30 //#include "flow.h"
   31 #include "detector_api.h"
   32 
   33 //#define  UNIT_TESTING
   34 
   35 #ifdef UNIT_TESTING
   36 #include "fw_appid.h"
   37 #endif
   38 
   39 typedef enum
   40 {
   41     SMTP_CLIENT_STATE_NONE,
   42     SMTP_CLIENT_STATE_HELO,
   43     SMTP_CLIENT_STATE_MAIL_FROM,
   44     SMTP_CLIENT_STATE_RCPT_TO,
   45     SMTP_CLIENT_STATE_DATA,
   46     SMTP_CLIENT_STATE_MESSAGE,
   47     SMTP_CLIENT_STATE_GET_PRODUCT_VERSION,
   48     SMTP_CLIENT_STATE_SKIP_LINE,
   49     SMTP_CLIENT_STATE_SKIP_SPACE,
   50     SMTP_CLIENT_STATE_SKIP_EOL,
   51     SMTP_CLIENT_STATE_CONNECTION_ERROR,
   52     SMTP_CLIENT_STATE_STARTTLS,
   53     SMTP_CLIENT_STATE_LOGIN_USER,
   54     SMTP_CLIENT_STATE_LOGIN_PASSWORD
   55 } SMTPClientState;
   56 
   57 #define MAX_HEADER_LINE_SIZE 1024
   58 
   59 #ifdef UNIT_TESTING
   60 char *stateName [] =
   61 {
   62     "SMTP_CLIENT_STATE_NONE",
   63     "SMTP_CLIENT_STATE_HELO",
   64     "SMTP_CLIENT_STATE_MAIL_FROM",
   65     "SMTP_CLIENT_STATE_RCPT_TO",
   66     "SMTP_CLIENT_STATE_DATA",
   67     "SMTP_CLIENT_STATE_MESSAGE",
   68     "SMTP_CLIENT_STATE_GET_PRODUCT_VERSION",
   69     "SMTP_CLIENT_STATE_SKIP_LINE",
   70     "SMTP_CLIENT_STATE_CONNECTION_ERROR",
   71     "SMTP_CLIENT_STATE_STARTTLS"
   72 };
   73 #endif
   74 
   75 /* flag values for ClientSMTPData */
   76 #define CLIENT_FLAG_STARTTLS_SUCCESS    0x01
   77 
   78 #define MAX_VERSION_SIZE    64
   79 #define SSL_WAIT_PACKETS    8  // This many un-decrypted packets without a HELO and we quit.
   80 typedef struct
   81 {
   82     int flags;
   83     SMTPClientState state;
   84     SMTPClientState nextstate;
   85     uint8_t version[MAX_VERSION_SIZE];
   86     unsigned pos;
   87     uint8_t     *headerline;
   88     int decryption_countdown;
   89 } ClientSMTPData;
   90 
   91 typedef struct _SMTP_CLIENT_APP_CONFIG
   92 {
   93     int enabled;
   94 } SMTP_CLIENT_APP_CONFIG;
   95 
   96 static SMTP_CLIENT_APP_CONFIG smtp_config;
   97 
   98 static CLIENT_APP_RETCODE smtp_ca_init(const InitClientAppAPI * const init_api, SF_LIST *config);
   99 static CLIENT_APP_RETCODE smtp_ca_validate(const uint8_t *data, uint16_t size, const int dir,
  100                                         tAppIdData *flowp, SFSnortPacket *pkt,
  101                                         struct _Detector *userData, const struct appIdConfig_ *pConfig);
  102 
  103 tRNAClientAppModule smtp_client_mod =
  104 {
  105     .name = "SMTP",
  106     .proto = IPPROTO_TCP,
  107     .init = &smtp_ca_init,
  108     .validate = &smtp_ca_validate,
  109     .minimum_matches = 1
  110 };
  111 
  112 typedef struct {
  113     const u_int8_t *pattern;
  114     unsigned length;
  115     int index;
  116     unsigned appId;
  117 } Client_App_Pattern;
  118 
  119 #define HELO "HELO"
  120 #define EHLO "EHLO"
  121 #define MAILFROM "MAIL FROM:"
  122 #define RCPTTO "RCPT TO:"
  123 #define DATA "DATA"
  124 #define RSET "RSET"
  125 #define AUTH "AUTH "
  126 #define AUTH_PLAIN "AUTH PLAIN"
  127 #define AUTH_LOGIN "AUTH LOGIN"
  128 #define STARTTLS "STARTTLS"
  129 
  130 #define STARTTLS_COMMAND_SUCCESS "220 "
  131 
  132 #define MICROSOFT "Microsoft"
  133 #define OUTLOOK "Outlook"
  134 #define EXPRESS "Express "
  135 #define IMO "IMO, "
  136 #define MAC "Mac"
  137 
  138 #define XMAILER "X-Mailer: "
  139 #define USERAGENT "User-Agent: "
  140 
  141 static const uint8_t APP_SMTP_OUTLOOK[] = "Microsoft Outlook";
  142 static const uint8_t APP_SMTP_OUTLOOK_MAC[] = "Microsoft-MacOutlook";
  143 static const uint8_t APP_SMTP_OUTLOOK_EXPRESS[] = "Microsoft Outlook Express ";
  144 static const uint8_t APP_SMTP_IMO[] = "IMO, ";
  145 static const uint8_t APP_SMTP_EVOLUTION[] = "Ximian Evolution ";
  146 static const uint8_t APP_SMTP_LOTUSNOTES[] =  "Lotus Notes ";
  147 static const uint8_t APP_SMTP_APPLEMAIL[] =  "Apple Mail (";
  148 static const uint8_t APP_SMTP_EUDORA[] =  "QUALCOMM Windows Eudora Version ";
  149 static const uint8_t APP_SMTP_EUDORAPRO[] =  "Windows Eudora Pro Version ";
  150 static const uint8_t APP_SMTP_AOL[] =  "AOL ";
  151 static const uint8_t APP_SMTP_MUTT[] =  "Mutt/";
  152 static const uint8_t APP_SMTP_KMAIL[] =  "KMail/";
  153 static const uint8_t APP_SMTP_MTHUNDERBIRD[] =  "Mozilla Thunderbird ";
  154 static const uint8_t APP_SMTP_THUNDERBIRD[] =  "Thunderbird ";
  155 static const uint8_t APP_SMTP_MOZILLA[] = "Mozilla";
  156 static const uint8_t APP_SMTP_THUNDERBIRD_SHORT[] = "Thunderbird/";
  157 
  158 static Client_App_Pattern patterns[] =
  159 {
  160     {(uint8_t *)HELO, sizeof(HELO)-1, 0, APP_ID_SMTP},
  161     {(uint8_t *)EHLO, sizeof(EHLO)-1, 0, APP_ID_SMTP},
  162     {APP_SMTP_OUTLOOK,         sizeof(APP_SMTP_OUTLOOK)-1,        -1, APP_ID_OUTLOOK},
  163     {APP_SMTP_OUTLOOK_MAC,     sizeof(APP_SMTP_OUTLOOK_MAC)-1,    -1, APP_ID_OUTLOOK},
  164     {APP_SMTP_OUTLOOK_EXPRESS, sizeof(APP_SMTP_OUTLOOK_EXPRESS)-1,-1, APP_ID_OUTLOOK_EXPRESS},
  165     {APP_SMTP_IMO,             sizeof(APP_SMTP_IMO)-1,            -1, APP_ID_SMTP_IMO},
  166     {APP_SMTP_EVOLUTION,       sizeof(APP_SMTP_EVOLUTION)-1,      -1, APP_ID_EVOLUTION},
  167     {APP_SMTP_LOTUSNOTES,      sizeof(APP_SMTP_LOTUSNOTES)-1,     -1, APP_ID_LOTUS_NOTES},
  168     {APP_SMTP_APPLEMAIL,       sizeof(APP_SMTP_APPLEMAIL)-1,      -1, APP_ID_APPLE_EMAIL},
  169     {APP_SMTP_EUDORA,          sizeof(APP_SMTP_EUDORA)-1,         -1, APP_ID_EUDORA},
  170     {APP_SMTP_EUDORAPRO,       sizeof(APP_SMTP_EUDORAPRO)-1,      -1, APP_ID_EUDORA_PRO},
  171     {APP_SMTP_AOL,             sizeof(APP_SMTP_AOL)-1,            -1, APP_ID_AOL_EMAIL},
  172     {APP_SMTP_MUTT,            sizeof(APP_SMTP_MUTT)-1,           -1, APP_ID_MUTT},
  173     {APP_SMTP_KMAIL,           sizeof(APP_SMTP_KMAIL)-1,          -1, APP_ID_KMAIL},
  174     {APP_SMTP_MTHUNDERBIRD,    sizeof(APP_SMTP_MTHUNDERBIRD)-1,   -1, APP_ID_THUNDERBIRD},
  175     {APP_SMTP_THUNDERBIRD,     sizeof(APP_SMTP_THUNDERBIRD)-1,    -1, APP_ID_THUNDERBIRD},
  176 };
  177 
  178 static tAppRegistryEntry clientAppIdRegistry[] =
  179 {
  180     {APP_ID_THUNDERBIRD, APPINFO_FLAG_CLIENT_ADDITIONAL},
  181     {APP_ID_OUTLOOK, APPINFO_FLAG_CLIENT_ADDITIONAL},
  182     {APP_ID_KMAIL, APPINFO_FLAG_CLIENT_ADDITIONAL},
  183     {APP_ID_EUDORA_PRO, APPINFO_FLAG_CLIENT_ADDITIONAL},
  184     {APP_ID_EVOLUTION, APPINFO_FLAG_CLIENT_ADDITIONAL},
  185     {APP_ID_SMTP_IMO, APPINFO_FLAG_CLIENT_ADDITIONAL},
  186     {APP_ID_EUDORA, APPINFO_FLAG_CLIENT_ADDITIONAL},
  187     {APP_ID_LOTUS_NOTES, APPINFO_FLAG_CLIENT_ADDITIONAL},
  188     {APP_ID_APPLE_EMAIL, APPINFO_FLAG_CLIENT_ADDITIONAL},
  189     {APP_ID_AOL_EMAIL, APPINFO_FLAG_CLIENT_ADDITIONAL},
  190     {APP_ID_MUTT, APPINFO_FLAG_CLIENT_ADDITIONAL},
  191     {APP_ID_SMTP, APPINFO_FLAG_CLIENT_ADDITIONAL},
  192     {APP_ID_OUTLOOK_EXPRESS, APPINFO_FLAG_CLIENT_ADDITIONAL},
  193     {APP_ID_SMTPS, APPINFO_FLAG_CLIENT_ADDITIONAL}
  194 };
  195 
  196 static CLIENT_APP_RETCODE smtp_ca_init(const InitClientAppAPI * const init_api, SF_LIST *config)
  197 {
  198     unsigned i;
  199     RNAClientAppModuleConfigItem *item;
  200 
  201     smtp_config.enabled = 1;
  202 
  203     if (config)
  204     {
  205         for (item = (RNAClientAppModuleConfigItem *)sflist_first(config);
  206              item;
  207              item = (RNAClientAppModuleConfigItem *)sflist_next(config))
  208         {
  209             _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value);
  210             if (strcasecmp(item->name, "enabled") == 0)
  211             {
  212                 smtp_config.enabled = atoi(item->value);
  213             }
  214         }
  215     }
  216 
  217     if (smtp_config.enabled)
  218     {
  219         for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++)
  220         {
  221             init_api->RegisterPattern(&smtp_ca_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig);
  222         }
  223     }
  224 
  225     unsigned j;
  226     for (j=0; j < sizeof(clientAppIdRegistry)/sizeof(*clientAppIdRegistry); j++)
  227     {
  228         _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",clientAppIdRegistry[j].appId);
  229         init_api->RegisterAppId(&smtp_ca_validate, clientAppIdRegistry[j].appId, clientAppIdRegistry[j].additionalInfo, init_api->pAppidConfig);
  230     }
  231 
  232     return CLIENT_APP_SUCCESS;
  233 }
  234 
  235 #define SMTP_PORT   25
  236 #define SMTP_CLOSING_CONN "closing connection\x0d\x0a"
  237 
  238 typedef enum
  239 {
  240     SMTP_SERVICE_STATE_NONE,
  241     SMTP_SERVICE_STATE_CONNECTION,
  242     SMTP_SERVICE_STATE_HELO,
  243     SMTP_SERVICE_STATE_BAD_CLIENT,
  244     SMTP_SERVICE_STATE_TRANSFER,
  245     SMTP_SERVICE_STATE_CONNECTION_ERROR,
  246     SMTP_SERVICE_STATE_STARTTLS,
  247     SMTP_SERVICE_STATE_SSL_HANDSHAKE
  248 } SMTPServiceState;
  249 
  250 typedef struct _SERVICE_SMTP_DATA
  251 {
  252     SMTPServiceState state;
  253     int code;
  254     int multiline;
  255     int set_flags;
  256 } ServiceSMTPData;
  257 
  258 #pragma pack(1)
  259 
  260 typedef struct _SERVICE_SMTP_CODE
  261 {
  262     uint8_t code[3];
  263     uint8_t sp;
  264 } ServiceSMTPCode;
  265 
  266 #pragma pack()
  267 
  268 static int smtp_svc_init(const InitServiceAPI * const init_api);
  269 static int smtp_svc_validate(ServiceValidationArgs* args);
  270 
  271 static tRNAServiceElement svc_element =
  272 {
  273     .next = NULL,
  274     .validate = &smtp_svc_validate,
  275     .detectorType = DETECTOR_TYPE_DECODER,
  276     .name = "smtp",
  277     .ref_count = 1,
  278     .current_ref_count = 1,
  279 };
  280 
  281 static RNAServiceValidationPort pp[] =
  282 {
  283     {&smtp_svc_validate, SMTP_PORT, IPPROTO_TCP},
  284     {NULL, 0, 0}
  285 };
  286 
  287 tRNAServiceValidationModule smtp_service_mod =
  288 {
  289     .name = "SMTP",
  290     .init = &smtp_svc_init,
  291     .pp = pp,
  292 };
  293 
  294 #define SMTP_PATTERN1 "220 "
  295 #define SMTP_PATTERN2 "220-"
  296 #define SMTP_PATTERN3 "SMTP"
  297 #define SMTP_PATTERN4 "smtp"
  298 
  299 static tAppRegistryEntry appIdRegistry[] =
  300 {
  301     {APP_ID_SMTP,  APPINFO_FLAG_SERVICE_ADDITIONAL},
  302     {APP_ID_SMTPS, APPINFO_FLAG_SERVICE_ADDITIONAL}
  303 };
  304 
  305 typedef struct _SMTP_DETECTOR_DATA
  306 {
  307     ClientSMTPData client;
  308     ServiceSMTPData server;
  309     int need_continue;
  310 } SMTPDetectorData;
  311 
  312 SF_SO_PUBLIC RNADetectorValidationModule smtp_detector_mod =
  313 {
  314     .service = &smtp_service_mod,
  315     .client = &smtp_client_mod,
  316 };
  317 
  318 static int smtp_svc_init(const InitServiceAPI * const init_api)
  319 {
  320     init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN1, sizeof(SMTP_PATTERN1)-1, 0, "smtp", init_api->pAppidConfig);
  321     init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN2, sizeof(SMTP_PATTERN2)-1, 0, "smtp", init_api->pAppidConfig);
  322     init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN3, sizeof(SMTP_PATTERN3)-1, -1, "smtp", init_api->pAppidConfig);
  323     init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN4, sizeof(SMTP_PATTERN4)-1, -1, "smtp", init_api->pAppidConfig);
  324 
  325     unsigned i;
  326     for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
  327     {
  328         _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId);
  329         init_api->RegisterAppId(&smtp_svc_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig);
  330     }
  331 
  332     return 0;
  333 }
  334 
  335 static int ExtractVersion(ClientSMTPData * const fd, const uint8_t *product,
  336                           const uint8_t *data, tAppIdData *flowp, SFSnortPacket *pkt, const int dir, const tAppIdConfig *pConfig)
  337 {
  338     const u_int8_t *p;
  339     u_int8_t *v;
  340     u_int8_t *v_end;
  341     unsigned len;
  342     unsigned sublen;
  343 
  344     v_end = fd->version;
  345     v_end += MAX_VERSION_SIZE - 1;
  346     len = data - product;
  347     if (len >= sizeof(MICROSOFT) && memcmp(product, MICROSOFT, sizeof(MICROSOFT)-1) == 0)
  348     {
  349         p = product + sizeof(MICROSOFT) - 1;
  350 
  351         if (*p == '-' || isspace(*p)) p++;
  352         else return 1;
  353 
  354         if (data-p >= (int)sizeof(MAC) && memcmp(p, MAC, sizeof(MAC)-1) == 0)
  355             p += sizeof(MAC) - 1;
  356 
  357         if (data-p >= (int)sizeof(OUTLOOK) && memcmp(p, OUTLOOK, sizeof(OUTLOOK)-1) == 0)
  358         {
  359             p += sizeof(OUTLOOK) - 1;
  360 
  361             if (*p == ',' || *p == '/' || isspace(*p))
  362             {
  363                 p++;
  364 
  365                 if (data-p >= (int)sizeof(EXPRESS) && memcmp(p, EXPRESS, sizeof(EXPRESS)-1) == 0)
  366                 {
  367                     p += sizeof(EXPRESS) - 1;
  368                     if (p >= data || isspace(*p)) return 1;
  369                     for (v=fd->version; v<v_end && p < data; v++,p++)
  370                     {
  371                         *v = *p;
  372                     }
  373                     *v = 0;
  374                     smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_OUTLOOK_EXPRESS, (char *)fd->version);
  375                     return 0;
  376                 }
  377                 else if (data-p >= (int)sizeof(IMO) && memcmp(p, IMO, sizeof(IMO)-1) == 0)
  378                 {
  379                     p += sizeof(IMO) - 1;
  380                     if (p >= data || isspace(*p)) return 1;
  381                     for (v=fd->version; v<v_end && p < data; v++,p++)
  382                     {
  383                         *v = *p;
  384                     }
  385                     *v = 0;
  386                     smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_SMTP_IMO, (char *)fd->version);
  387                     return 0;
  388                 }
  389                 else
  390                 {
  391                     if (p >= data || isspace(*p)) return 1;
  392                     for (v=fd->version; v<v_end && p < data; v++,p++)
  393                     {
  394                         *v = *p;
  395                     }
  396                     *v = 0;
  397                     smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_OUTLOOK, (char *)fd->version);
  398                     return 0;
  399                 }
  400             }
  401         }
  402     }
  403     else if (len >= sizeof(APP_SMTP_EVOLUTION) && memcmp(product, APP_SMTP_EVOLUTION, sizeof(APP_SMTP_EVOLUTION)-1) == 0)
  404     {
  405         p = product + sizeof(APP_SMTP_EVOLUTION) - 1;
  406         if (p >= data || isspace(*p)) return 1;
  407         for (v=fd->version; v<v_end && p < data; v++,p++)
  408         {
  409             *v = *p;
  410         }
  411         *v = 0;
  412         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_EVOLUTION, (char *)fd->version);
  413         return 0;
  414     }
  415     else if (len >= sizeof(APP_SMTP_LOTUSNOTES) && memcmp(product, APP_SMTP_LOTUSNOTES, sizeof(APP_SMTP_LOTUSNOTES)-1) == 0)
  416     {
  417         p = product + sizeof(APP_SMTP_LOTUSNOTES) - 1;
  418         if (p >= data || isspace(*p)) return 1;
  419         for (v=fd->version; v<v_end && p < data; v++,p++)
  420         {
  421             *v = *p;
  422         }
  423         *v = 0;
  424         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_LOTUS_NOTES, (char *)fd->version);
  425         return 0;
  426     }
  427     else if (len >= sizeof(APP_SMTP_APPLEMAIL) && memcmp(product, APP_SMTP_APPLEMAIL, sizeof(APP_SMTP_APPLEMAIL)-1) == 0)
  428     {
  429         p = product + sizeof(APP_SMTP_APPLEMAIL) - 1;
  430         if (p >= data || *(data - 1) != ')' || *p == ')' || isspace(*p)) return 1;
  431         for (v=fd->version; v<v_end && p < data-1; v++,p++)
  432         {
  433             *v = *p;
  434         }
  435         *v = 0;
  436         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_APPLE_EMAIL, (char *)fd->version);
  437         return 0;
  438     }
  439     else if (len >= sizeof(APP_SMTP_EUDORA) && memcmp(product, APP_SMTP_EUDORA, sizeof(APP_SMTP_EUDORA)-1) == 0)
  440     {
  441         p = product + sizeof(APP_SMTP_EUDORA) - 1;
  442         if (p >= data || isspace(*p)) return 1;
  443         for (v=fd->version; v<v_end && p < data; v++,p++)
  444         {
  445             *v = *p;
  446         }
  447         *v = 0;
  448         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_EUDORA, (char *)fd->version);
  449         return 0;
  450     }
  451     else if (len >= sizeof(APP_SMTP_EUDORAPRO) && memcmp(product, APP_SMTP_EUDORAPRO, sizeof(APP_SMTP_EUDORAPRO)-1) == 0)
  452     {
  453         p = product + sizeof(APP_SMTP_EUDORAPRO) - 1;
  454         if (p >= data || isspace(*p)) return 1;
  455         for (v=fd->version; v<v_end && p < data; v++,p++)
  456         {
  457             *v = *p;
  458         }
  459         *v = 0;
  460         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_EUDORA_PRO, (char *)fd->version);
  461         return 0;
  462     }
  463     else if (len >= sizeof(APP_SMTP_AOL) && memcmp(product, APP_SMTP_AOL, sizeof(APP_SMTP_AOL)-1) == 0)
  464     {
  465         p = product + sizeof(APP_SMTP_AOL) - 1;
  466         if (p >= data || isspace(*p)) return 1;
  467         for (v=fd->version; v<v_end && p < data; v++,p++)
  468         {
  469             *v = *p;
  470         }
  471         *v = 0;
  472         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_AOL_EMAIL, (char *)fd->version);
  473         return 0;
  474     }
  475     else if (len >= sizeof(APP_SMTP_MUTT) && memcmp(product, APP_SMTP_MUTT, sizeof(APP_SMTP_MUTT)-1) == 0)
  476     {
  477         p = product + sizeof(APP_SMTP_MUTT) - 1;
  478         if (p >= data || isspace(*p)) return 1;
  479         for (v=fd->version; v<v_end && p < data; v++,p++)
  480         {
  481             *v = *p;
  482         }
  483         *v = 0;
  484         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_MUTT, (char *)fd->version);
  485         return 0;
  486     }
  487     else if (len >= sizeof(APP_SMTP_KMAIL) && memcmp(product, APP_SMTP_KMAIL, sizeof(APP_SMTP_KMAIL)-1) == 0)
  488     {
  489         p = product + sizeof(APP_SMTP_KMAIL) - 1;
  490         if (p >= data || isspace(*p)) return 1;
  491         for (v=fd->version; v<v_end && p < data; v++,p++)
  492         {
  493             *v = *p;
  494         }
  495         *v = 0;
  496         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_SMTP/*KMAIL_ID*/, (char *)fd->version);
  497         return 0;
  498     }
  499     else if (len >= sizeof(APP_SMTP_THUNDERBIRD) && memcmp(product, APP_SMTP_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD)-1) == 0)
  500     {
  501         p = product + sizeof(APP_SMTP_THUNDERBIRD) - 1;
  502         if (p >= data || isspace(*p)) return 1;
  503         for (v=fd->version; v<v_end && p < data; v++,p++)
  504         {
  505             *v = *p;
  506         }
  507         *v = 0;
  508         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_THUNDERBIRD, (char *)fd->version);
  509         return 0;
  510     }
  511     else if (len >= sizeof(APP_SMTP_MTHUNDERBIRD) && memcmp(product, APP_SMTP_MTHUNDERBIRD, sizeof(APP_SMTP_MTHUNDERBIRD)-1) == 0)
  512     {
  513         p = product + sizeof(APP_SMTP_MTHUNDERBIRD) - 1;
  514         if (p >= data || isspace(*p)) return 1;
  515         for (v=fd->version; v<v_end && p < data; v++,p++)
  516         {
  517             *v = *p;
  518         }
  519         *v = 0;
  520         smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_THUNDERBIRD, (char *)fd->version);
  521         return 0;
  522     }
  523     else if (len >= sizeof(APP_SMTP_MOZILLA) && memcmp(product, APP_SMTP_MOZILLA, sizeof(APP_SMTP_MOZILLA)-1) == 0)
  524     {
  525         for (p = product + sizeof(APP_SMTP_MOZILLA) - 1; p < data; p++)
  526         {
  527             if (*p == 'T')
  528             {
  529                 sublen = data - p;
  530                 if (sublen >= sizeof(APP_SMTP_THUNDERBIRD_SHORT) && memcmp(p, APP_SMTP_THUNDERBIRD_SHORT, sizeof(APP_SMTP_THUNDERBIRD_SHORT)-1) == 0)
  531                 {
  532                     p = p + sizeof(APP_SMTP_THUNDERBIRD_SHORT) - 1;
  533                     for (v=fd->version; v<v_end && p < data; p++)
  534                     {
  535                         if (*p == 0x0A || *p == 0x0D || !isprint(*p)) break;
  536                         *v = *p;
  537                         v++;
  538                     }
  539                     *v = 0;
  540                     smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_THUNDERBIRD, (char *)fd->version);
  541                     return 0;
  542                 }
  543             }
  544         }
  545     }
  546 
  547     return 1;
  548 }
  549 static void smtp_free_state(void *data)
  550 {
  551     SMTPDetectorData *dd = (SMTPDetectorData *)data;
  552     ClientSMTPData *cd;
  553 
  554     if (dd)
  555     {
  556         cd = &dd->client;
  557         if (cd->headerline)
  558             free(cd->headerline);
  559         free(dd);
  560     }
  561 }
  562 static inline SMTPDetectorData *smtp_get_SMTPDetectorData(tAppIdData *flowp)
  563 {
  564     SMTPDetectorData *dd = smtp_detector_mod.api->data_get(flowp, smtp_detector_mod.flow_data_index);
  565     if (dd)
  566         return dd;
  567 
  568     if ((dd =  calloc(1, sizeof(*dd))) == NULL)
  569         return NULL;
  570     if (smtp_detector_mod.api->data_add(flowp, dd, smtp_detector_mod.flow_data_index, &smtp_free_state))
  571     {
  572         free(dd);
  573         return NULL;
  574     }
  575     dd->server.state = SMTP_SERVICE_STATE_CONNECTION;
  576     dd->client.state = SMTP_CLIENT_STATE_HELO;
  577     dd->need_continue = 1;
  578     setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS);
  579     return dd;
  580 }
  581 
  582 // #define UNIT_TEST_SKIP
  583 static CLIENT_APP_RETCODE smtp_ca_validate(const uint8_t *data, uint16_t size, const int dir,
  584                                         tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData,
  585                                         const struct appIdConfig_ *pConfig)
  586 {
  587     SMTPDetectorData *dd;
  588     ClientSMTPData *fd;
  589     const uint8_t *end;
  590     unsigned len;
  591     int line_break = 0;
  592     SMTPServiceState serviceState;
  593 #ifdef UNIT_TESTING
  594     SMTPClientState currState = SMTP_CLIENT_STATE_NONE;
  595 #endif
  596 
  597 #ifdef APP_ID_USES_REASSEMBLED
  598     smtp_detector_mod.streamAPI->response_flush_stream(pkt);
  599 #endif
  600     if ((dd = smtp_get_SMTPDetectorData(flowp)) == NULL)
  601         return CLIENT_APP_ENOMEM;
  602 
  603     if (dir != APP_ID_FROM_INITIATOR)
  604         return CLIENT_APP_INPROCESS;
  605 
  606     fd = &dd->client;
  607 
  608     if (getAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_DECRYPTED) == APPID_SESSION_ENCRYPTED)
  609     {
  610         if ((fd->flags & CLIENT_FLAG_STARTTLS_SUCCESS))
  611         {
  612             fd->decryption_countdown--;
  613             if (!fd->decryption_countdown)
  614 #ifdef UNIT_TEST_SKIP
  615             if (flowp->session_packet_count == 0)
  616 #endif
  617             {
  618                 /* Because we can't see any further info without decryption we settle for
  619                    plain APP_ID_SMTPS instead of perhaps finding data that would make calling
  620                    ExtractVersion() worthwhile, So set the appid and call it good. */
  621                 smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTPS, APP_ID_SMTPS, NULL);
  622                 goto done;
  623             }
  624         }
  625         return CLIENT_APP_INPROCESS;
  626     }
  627 
  628 
  629     for (end = data + size; data < end; data++)
  630     {
  631 #ifdef UNIT_TESTING
  632         if (app_id_debug_session_flag && currState != fd->state)
  633         {
  634             DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppIdDbg %s SMTP client state %s\n", app_id_debug_session, stateName[fd->state]););
  635             currState = fd->state;
  636         }
  637 #endif
  638         len = end - data;
  639         switch (fd->state)
  640         {
  641         case SMTP_CLIENT_STATE_HELO:
  642             if (len >= (sizeof(HELO)-1) && strncasecmp((const char *)data, HELO, sizeof(HELO)-1) == 0)
  643             {
  644                 data += (sizeof(HELO)-1)-1;
  645                 fd->nextstate = SMTP_CLIENT_STATE_MAIL_FROM;
  646                 fd->state = SMTP_CLIENT_STATE_SKIP_SPACE;
  647                 fd->flags &= ~ CLIENT_FLAG_STARTTLS_SUCCESS;
  648             }
  649             else if (len >= (sizeof(EHLO)-1) && strncasecmp((const char *)data, EHLO, sizeof(EHLO)-1) == 0)
  650             {
  651                 data += (sizeof(EHLO)-1)-1;
  652                 fd->nextstate = SMTP_CLIENT_STATE_MAIL_FROM;
  653                 fd->state = SMTP_CLIENT_STATE_SKIP_SPACE;
  654                 fd->flags &= ~ CLIENT_FLAG_STARTTLS_SUCCESS;
  655             }
  656             else goto done;
  657             break;
  658 
  659         case SMTP_CLIENT_STATE_MAIL_FROM:
  660             serviceState = dd->server.state;
  661             if (len >= (sizeof(MAILFROM)-1) && strncasecmp((const char *)data, MAILFROM, sizeof(MAILFROM)-1) == 0)
  662             {
  663                 data += (sizeof(MAILFROM)-1)-1;
  664                 fd->nextstate = SMTP_CLIENT_STATE_RCPT_TO;
  665                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  666             }
  667             else if (len >= (sizeof(RSET)-1) && strncasecmp((const char *)data, RSET, sizeof(RSET)-1) == 0)
  668             {
  669                 data += (sizeof(RSET)-1)-1;
  670                 fd->nextstate = fd->state;
  671                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  672             }
  673             else if (len >= (sizeof(AUTH_PLAIN)-1) && strncasecmp((const char *)data, AUTH_PLAIN, sizeof(AUTH_PLAIN)-1) == 0)
  674             {
  675                 data += (sizeof(AUTH_PLAIN)-1)-1;
  676                 fd->nextstate = fd->state;
  677                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  678             }
  679             else if (len >= (sizeof(AUTH_LOGIN)-1) && strncasecmp((const char *)data, AUTH_LOGIN, sizeof(AUTH_LOGIN)-1) == 0)
  680             {
  681                 data += (sizeof(AUTH_LOGIN)-1)-1;
  682                 fd->nextstate = SMTP_CLIENT_STATE_LOGIN_USER;
  683                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  684             }
  685             else if (len >= (sizeof(AUTH)-1) && strncasecmp((const char *)data, AUTH, sizeof(AUTH)-1) == 0)
  686             {
  687                 data += (sizeof(AUTH)-1)-1;
  688                 fd->nextstate = fd->state;
  689                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  690             }
  691             else if (len >= (sizeof(STARTTLS)-1) && strncasecmp((const char *)data, STARTTLS, sizeof(STARTTLS)-1) == 0)
  692             {
  693                 data += (sizeof(STARTTLS)-1)-1;
  694                 serviceState = dd->server.state = SMTP_SERVICE_STATE_STARTTLS;
  695                 fd->nextstate = fd->state;
  696                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  697             }
  698             /* check for state reversion */
  699             else if (len >= (sizeof(HELO)-1) && strncasecmp((const char *)data, HELO, sizeof(HELO)-1) == 0)
  700             {
  701                 data += (sizeof(HELO)-1)-1;
  702                 fd->nextstate = fd->state;
  703                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  704                 dd->server.state = SMTP_SERVICE_STATE_HELO; // make sure that service side expects the 250
  705             }
  706             else if (len >= (sizeof(EHLO)-1) && strncasecmp((const char *)data, EHLO, sizeof(EHLO)-1) == 0)
  707             {
  708                 data += (sizeof(EHLO)-1)-1;
  709                 fd->nextstate = fd->state;
  710                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  711                 dd->server.state = SMTP_SERVICE_STATE_HELO; // make sure that service side expects the 250
  712             }
  713             else goto done;
  714             if (serviceState == SMTP_SERVICE_STATE_TRANSFER)
  715             {
  716                 setAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  717                 smtp_service_mod.api->add_service(flowp, pkt, dir, &svc_element,
  718                                                   APP_ID_SMTP, NULL, NULL, NULL, NULL);
  719             }
  720             break;
  721         case SMTP_CLIENT_STATE_LOGIN_USER:
  722             {
  723                 fd->nextstate = SMTP_CLIENT_STATE_LOGIN_PASSWORD;
  724                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  725             }
  726             break;
  727         case SMTP_CLIENT_STATE_LOGIN_PASSWORD:
  728             {
  729                 fd->nextstate = SMTP_CLIENT_STATE_MAIL_FROM;
  730                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  731             }
  732             break;
  733         case SMTP_CLIENT_STATE_RCPT_TO:
  734             if (len >= (sizeof(RCPTTO)-1) && strncasecmp((const char *)data, RCPTTO, sizeof(RCPTTO)-1) == 0)
  735             {
  736                 data += (sizeof(RCPTTO)-1)-1;
  737                 fd->nextstate = SMTP_CLIENT_STATE_DATA;
  738                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  739             }
  740             else
  741                 goto done;
  742             break;
  743 
  744         case SMTP_CLIENT_STATE_DATA:
  745             if (len >= (sizeof(DATA)-1) && strncasecmp((const char *)data, DATA, sizeof(DATA)-1) == 0)
  746             {
  747                 data += (sizeof(DATA)-1)-1;
  748                 fd->nextstate = SMTP_CLIENT_STATE_MESSAGE;
  749                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  750             }
  751             else if (len >= (sizeof(RCPTTO)-1) && strncasecmp((const char *)data, RCPTTO, sizeof(RCPTTO)-1) == 0)
  752             {
  753                 data += (sizeof(RCPTTO)-1)-1;
  754                 fd->nextstate = fd->state;
  755                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  756             }
  757             break;
  758         case SMTP_CLIENT_STATE_MESSAGE:
  759             if (*data == '.')
  760             {
  761                 if (len == 0 ||
  762                     (len >= 1 && data[1] == '\n') ||
  763                     (len >= 2 && data[1] == '\r' && data[2] == '\n'))
  764                 {
  765                     smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_SMTP, NULL);
  766                     goto done;
  767                 }
  768             }
  769             else if (len >= (sizeof(XMAILER)-1) && strncasecmp((const char *)data, XMAILER, sizeof(XMAILER)-1) == 0)
  770             {
  771                 data += (sizeof(XMAILER)-1)-1;
  772                 fd->state = SMTP_CLIENT_STATE_GET_PRODUCT_VERSION;
  773             }
  774             else if (len >= (sizeof(USERAGENT)-1) && strncasecmp((const char *)data, USERAGENT, sizeof(USERAGENT)-1) == 0)
  775             {
  776                 data += (sizeof(USERAGENT)-1)-1;
  777                 fd->state = SMTP_CLIENT_STATE_GET_PRODUCT_VERSION;
  778             }
  779             else if (!isprint(*data) && *data != '\t')
  780                 goto done;
  781             else
  782             {
  783                 fd->nextstate = fd->state;
  784                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  785             }
  786             break;
  787 
  788         case SMTP_CLIENT_STATE_GET_PRODUCT_VERSION:
  789             if (!fd->headerline)
  790             {
  791                 if (!(fd->headerline = malloc(MAX_HEADER_LINE_SIZE)))
  792                     goto done;
  793             }
  794             while((data < end) && (fd->pos < (MAX_HEADER_LINE_SIZE-1)))
  795             {
  796                 if ((*data == ' ') || (*data == '\t'))
  797                 {
  798                     line_break = 0;
  799                     fd->headerline[fd->pos++] = *data;
  800                 }
  801                 else if((*data == '\n') && (line_break != 1))
  802                 {
  803                     /* Can't have multiple LFs in a row, but if we get one it
  804                      * needs to be followed by at least one space */
  805                     line_break = 1;
  806                 }
  807                 else if(*data == '\r')
  808                 {
  809                     // CR needs to be followed by LF and can't start a line
  810                     line_break = 2;
  811                 }
  812                 else if (!isprint(*data))
  813                 {
  814                     free(fd->headerline);
  815                     fd->headerline = NULL;
  816                     fd->pos = 0;
  817                     goto done;
  818                 }
  819                 else if(!line_break)
  820                 {
  821                     fd->headerline[fd->pos++] = *data;
  822                 }
  823                 else
  824                 {
  825                      // We have reached the end of the header
  826                      break;
  827                 }
  828                 data++;
  829             }
  830             data--;
  831             if (line_break || fd->pos >= (MAX_HEADER_LINE_SIZE-1))
  832             {
  833                 ExtractVersion(fd, fd->headerline, fd->headerline + fd->pos, flowp, pkt, dir, pConfig);
  834                 free(fd->headerline);
  835                 fd->headerline = NULL;
  836                 fd->pos = 0;
  837                 goto done;
  838             }
  839             break;
  840 
  841         case SMTP_CLIENT_STATE_SKIP_SPACE:
  842             if (*data == ' ')
  843             {
  844                 fd->state = SMTP_CLIENT_STATE_SKIP_LINE;
  845             }
  846             else if (*data == '\n')
  847             {
  848                 fd->pos = 0;
  849                 fd->state = fd->nextstate;
  850                 fd->nextstate = SMTP_CLIENT_STATE_NONE;
  851             }
  852             else if (*data == '\r')
  853                 fd->state = SMTP_CLIENT_STATE_SKIP_EOL;
  854             else
  855                 goto done;
  856             break;
  857 
  858         case SMTP_CLIENT_STATE_SKIP_EOL:
  859             if (*data == '\n')
  860             {
  861                 fd->pos = 0;
  862                 fd->state = fd->nextstate;
  863                 fd->nextstate = SMTP_CLIENT_STATE_NONE;
  864             }
  865             else
  866                 goto done;
  867             break;
  868 
  869         case SMTP_CLIENT_STATE_SKIP_LINE:
  870             if (*data == '\n')
  871             {
  872                 fd->pos = 0;
  873                 fd->state = fd->nextstate;
  874                 fd->nextstate = SMTP_CLIENT_STATE_NONE;
  875             }
  876             else if (!(*data == '\r' || isprint(*data)))
  877                 goto done;
  878             break;
  879 
  880         default:
  881             goto done;
  882         }
  883     }
  884     return CLIENT_APP_INPROCESS;
  885 
  886 done:
  887     dd->need_continue = 0;
  888     if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  889         clearAppIdFlag(flowp, APPID_SESSION_CONTINUE | APPID_SESSION_CLIENT_GETS_SERVER_PACKETS);
  890     else
  891         clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS);
  892     setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
  893     return CLIENT_APP_SUCCESS;
  894 }
  895 
  896 static inline int smtp_validate_reply(const uint8_t *data, uint16_t *offset,
  897                                uint16_t size, int *multi, int *code)
  898 {
  899     const ServiceSMTPCode *code_hdr;
  900     int tmp;
  901 
  902     /* Trim any blank lines (be a little tolerant) */
  903     for (; *offset<size; (*offset)++)
  904     {
  905         if (data[*offset] != 0x0D && data[*offset] != 0x0A) break;
  906     }
  907 
  908     if (size - *offset < (int)sizeof(ServiceSMTPCode))
  909     {
  910         for (; *offset<size; (*offset)++)
  911         {
  912             if (!isspace(data[*offset])) return -1;
  913         }
  914         return 0;
  915     }
  916 
  917     code_hdr = (ServiceSMTPCode *)(data + *offset);
  918 
  919     if (code_hdr->code[0] < '1' || code_hdr->code[0] > '5') return -1;
  920     tmp = (code_hdr->code[0] - '0') * 100;
  921 
  922     if (code_hdr->code[1] < '0' || code_hdr->code[1] > '5') return -1;
  923     tmp += (code_hdr->code[1] - '0') * 10;
  924 
  925     if (!isdigit(code_hdr->code[2])) return -1;
  926     tmp += code_hdr->code[2] - '0';
  927 
  928     if (*multi && tmp != *code) return -1;
  929     *code = tmp;
  930     if (code_hdr->sp == '-') *multi = 1;
  931     else if (code_hdr->sp == ' ') *multi = 0;
  932     else return -1;
  933 
  934     /* We have a valid code, now we need to see if the rest of the line
  935         is okay */
  936 
  937     *offset += sizeof(ServiceSMTPCode);
  938     for (; *offset < size; (*offset)++)
  939     {
  940         if (data[*offset] == 0x0D)
  941         {
  942             (*offset)++;
  943             if (*offset >= size) return -1;
  944             if (data[*offset] != 0x0A) return -1;
  945         }
  946         if (data[*offset] == 0x0A)
  947         {
  948             if (*multi)
  949             {
  950                 if ((*offset + 1) >= size) return 0;
  951 
  952                 if (size - (*offset + 1) < (int)sizeof(ServiceSMTPCode)) return -1;
  953 
  954                 code_hdr = (ServiceSMTPCode *)(data + *offset + 1);
  955 
  956                 if (code_hdr->code[0] < '1' || code_hdr->code[0] > '5')
  957                     return -1;
  958                 tmp = (code_hdr->code[0] - '0') * 100;
  959 
  960                 if (code_hdr->code[1] < '1' || code_hdr->code[1] > '5')
  961                     return -1;
  962                 tmp += (code_hdr->code[1] - '0') * 10;
  963 
  964                 if (!isdigit(code_hdr->code[2])) return -1;
  965                 tmp += code_hdr->code[2] - '0';
  966 
  967                 if (tmp != *code) return -1;
  968 
  969                 if (code_hdr->sp == ' ') *multi = 0;
  970                 else if (code_hdr->sp != '-') return -1;
  971 
  972                 *offset += sizeof(ServiceSMTPCode);
  973             }
  974             else
  975             {
  976                 (*offset)++;
  977                 return *code;
  978             }
  979         }
  980         else if (!isprint(data[*offset])) return -1;
  981     }
  982 
  983 
  984     return 0;
  985 }
  986 
  987 static int smtp_svc_validate(ServiceValidationArgs* args)
  988 {
  989     SMTPDetectorData *dd;
  990     ServiceSMTPData *fd;
  991     tAppIdData *flowp = args->flowp;
  992     const uint8_t *data = args->data;
  993     uint16_t size = args->size;
  994     uint16_t offset;
  995 
  996 #ifdef APP_ID_USES_REASSEMBLED
  997     pop3_detector_mod.streamAPI->response_flush_stream(pkt);
  998 #endif
  999 
 1000     if ((dd = smtp_get_SMTPDetectorData(flowp)) == NULL)
 1001         return SERVICE_ENOMEM;
 1002 
 1003     if (!size)
 1004         goto inprocess;
 1005 
 1006     if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
 1007     {
 1008         if (!dd->need_continue)
 1009             clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
 1010         return SERVICE_SUCCESS; // client made the decision so we are totally done
 1011     }
 1012 
 1013     fd = &dd->server;
 1014 
 1015     if (args->dir != APP_ID_FROM_RESPONDER)
 1016     {
 1017         if (SMTP_SERVICE_STATE_HELO == fd->state)
 1018         {
 1019             if (!((size >= (sizeof(HELO)-1) && strncasecmp((const char *)data, HELO, sizeof(HELO)-1) == 0) ||
 1020                   (size >= (sizeof(EHLO)-1) && strncasecmp((const char *)data, EHLO, sizeof(EHLO)-1) == 0)))
 1021             {
 1022                 fd->state = SMTP_SERVICE_STATE_BAD_CLIENT;
 1023             }
 1024         }
 1025         goto inprocess;
 1026     }
 1027 
 1028     offset = 0;
 1029     while (offset < size)
 1030     {
 1031         if (smtp_validate_reply(data, &offset, size, &fd->multiline, &fd->code) < 0)
 1032         {
 1033             if (!(dd->client.flags & CLIENT_FLAG_STARTTLS_SUCCESS))
 1034                 goto fail;
 1035             goto inprocess;
 1036         }
 1037         if (!fd->code) goto inprocess;
 1038         switch (fd->state)
 1039         {
 1040         case SMTP_SERVICE_STATE_CONNECTION:
 1041             switch (fd->code)
 1042             {
 1043             case 220:
 1044                 fd->state = SMTP_SERVICE_STATE_HELO;
 1045                 break;
 1046             case 421:
 1047                 if (service_strstr(data, size, (const uint8_t *)SMTP_CLOSING_CONN, sizeof(SMTP_CLOSING_CONN)-1))
 1048                     goto success;
 1049                 goto fail;
 1050             case 554:
 1051                 goto success;
 1052             default:
 1053                 goto fail;
 1054             }
 1055             break;
 1056         case SMTP_SERVICE_STATE_HELO:
 1057             switch (fd->code)
 1058             {
 1059             case 250:
 1060                 fd->state = SMTP_SERVICE_STATE_TRANSFER;
 1061                 break;
 1062             case 220:
 1063             case 500:
 1064             case 501:
 1065             case 502:
 1066             case 504:
 1067                 break;
 1068             case 421:
 1069             case 553:
 1070                 fd->state = SMTP_SERVICE_STATE_CONNECTION_ERROR;
 1071                 break;
 1072             default:
 1073                 goto fail;
 1074             }
 1075             break;
 1076         case SMTP_SERVICE_STATE_STARTTLS:
 1077             // success or fail, return client to connection-complete state.
 1078             dd->client.state = SMTP_CLIENT_STATE_HELO;
 1079             fd->state = SMTP_SERVICE_STATE_HELO;
 1080             if (fd->code == 220)
 1081             {
 1082                 dd->client.flags |= CLIENT_FLAG_STARTTLS_SUCCESS;
 1083                 if (_dpd.isSSLPolicyEnabled(NULL))
 1084                     dd->client.decryption_countdown = SSL_WAIT_PACKETS; // max wait if decryption fails (e.g., cert error)
 1085                 else
 1086                     dd->client.decryption_countdown = 1; // no wait for decryption
 1087                 smtp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
 1088                                                   APP_ID_SMTPS, NULL, NULL, NULL, NULL);
 1089                 if (dd->need_continue > 0)
 1090                     setAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_STICKY_SERVICE | APPID_SESSION_CONTINUE);
 1091                 else
 1092                     setAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_STICKY_SERVICE);
 1093                 return SERVICE_SUCCESS;
 1094             }
 1095             /* STARTTLS failed. Fall through and call this SMTP */
 1096         case SMTP_SERVICE_STATE_TRANSFER:
 1097             goto success;
 1098         case SMTP_SERVICE_STATE_BAD_CLIENT:
 1099             switch (fd->code)
 1100             {
 1101             case 500:
 1102             case 501:
 1103             case 502:
 1104             case 550:
 1105                 goto not_compatible;
 1106             }
 1107         case SMTP_SERVICE_STATE_CONNECTION_ERROR:
 1108         default:
 1109             goto fail;
 1110         }
 1111     }
 1112 
 1113 inprocess:
 1114     smtp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL);
 1115     return SERVICE_INPROCESS;
 1116 
 1117 success:
 1118     if (dd->need_continue > 0)
 1119         setAppIdFlag(flowp, APPID_SESSION_CONTINUE);
 1120 
 1121     smtp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
 1122                                       APP_ID_SMTP, NULL, NULL, NULL, NULL);
 1123     return SERVICE_SUCCESS;
 1124 
 1125 fail:
 1126     smtp_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element,
 1127                                        smtp_service_mod.flow_data_index, args->pConfig, NULL);
 1128     return SERVICE_NOMATCH;
 1129 
 1130 not_compatible:
 1131     smtp_service_mod.api->incompatible_data(flowp, args->pkt, args->dir, &svc_element,
 1132                                             smtp_service_mod.flow_data_index, args->pConfig, NULL);
 1133     return SERVICE_NOT_COMPATIBLE;
 1134 }