]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #613 in SNORT/snort3 from appid_client_smtp5 to master
authorShawn Turner (shaturne) <shaturne@cisco.com>
Wed, 7 Sep 2016 17:13:21 +0000 (13:13 -0400)
committerShawn Turner (shaturne) <shaturne@cisco.com>
Wed, 7 Sep 2016 17:13:21 +0000 (13:13 -0400)
Squashed commit of the following:

commit 244930cb9245e978861f61ee0387c726fc1974e5
Author: Steve Chew <stechew@cisco.com>
Date:   Tue Sep 6 11:50:55 2016 -0400

    Added smtp client counters and unit tests.

configure.ac
src/network_inspectors/appid/CMakeLists.txt
src/network_inspectors/appid/Makefile.am
src/network_inspectors/appid/appid_module.cc
src/network_inspectors/appid/appid_module.h
src/network_inspectors/appid/client_plugins/client_app_smtp.cc
src/network_inspectors/appid/client_plugins/test/CMakeLists.txt [new file with mode: 0644]
src/network_inspectors/appid/client_plugins/test/Makefile.am [new file with mode: 0644]
src/network_inspectors/appid/client_plugins/test/client_app_smtp_test.cc [new file with mode: 0644]

index c76f850b2f639a144399016b53b6e105653e6f7a..53eaef708138e6b17f0a028f1c44f111f179c330 100644 (file)
@@ -1158,6 +1158,7 @@ src/stream/user/Makefile \
 src/stream/file/Makefile \
 src/network_inspectors/Makefile \
 src/network_inspectors/appid/Makefile \
+src/network_inspectors/appid/client_plugins/test/Makefile \
 src/network_inspectors/appid/service_plugins/test/Makefile \
 src/network_inspectors/arp_spoof/Makefile \
 src/network_inspectors/binder/Makefile \
index 8b12ffd539aebbacc1966a71c56b4f6fa98fbfb3..0646818a9bcdaf46e692b46053b89e21093f2a27 100644 (file)
@@ -198,6 +198,7 @@ endif (STATIC_INSPECTORS)
 target_include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} )
 
 add_subdirectory(service_plugins/test)
+add_subdirectory(client_plugins/test)
 
 #install (FILES ${APPID_INCLUDES}
 #    DESTINATION "${INCLUDE_INSTALL_PATH}/appid"
index dde53b8df679ef8904c9bca8f95791e1e8d70f3d..fc2cfbb64d8b03c66ccb766b9e81ab8ceede31cb 100644 (file)
@@ -191,6 +191,7 @@ $(util_file_list)
 endif
 
 if ENABLE_UNIT_TESTS
-SUBDIRS=service_plugins/test
+SUBDIRS=service_plugins/test \
+client_plugins/test
 endif
 
index 248606683c30ac39062371243c7ee931f8cc496d..d5db3cbb32e9c1f64f009d5b67455c1b1a258d38 100644 (file)
@@ -64,6 +64,18 @@ const PegInfo appid_pegs[] =
     { "netbios_flows", "count of netbios service flows discovered by appid" },
     { "pop_flows", "count of pop service flows discovered by appid" },
     { "rsync_flows", "count of rsync service flows discovered by appid" },
+    { "smtp_aol_clients", "count of AOL smtp clients discovered by appid" },
+    { "smtp_applemail_clients", "count of Apple Mail smtp clients discovered by appid" },
+    { "smtp_eudora_clients", "count of Eudora smtp clients discovered by appid" },
+    { "smtp_eudora_pro_clients", "count of Eudora Pro smtp clients discovered by appid" },
+    { "smtp_evolution_clients", "count of Evolution smtp clients discovered by appid" },
+    { "smtp_kmail_clients", "count of KMail smtp clients discovered by appid" },
+    { "smtp_lotus_notes_clients", "count of Lotus Notes smtp clients discovered by appid" },
+    { "smtp_microsoft_outlook_clients", "count of Microsoft Outlook smtp clients discovered by appid" },
+    { "smtp_microsoft_outlook_express_clients", "count of Microsoft Outlook Express smtp clients discovered by appid" },
+    { "smtp_microsoft_outlook_imo_clients", "count of Microsoft Outlook IMO smtp clients discovered by appid" },
+    { "smtp_mutt_clients", "count of Mutt smtp clients discovered by appid" },
+    { "smtp_thunderbird_clients", "count of Thunderbird smtp clients discovered by appid" },
     { "smtp_flows", "count of smtp flows discovered by appid" },
     { "smtps_flows", "count of smtps flows discovered by appid" },
     { "ssh_clients", "count of ssh clients discovered by appid" },
index 056555b7cf1f8af0226424764b518815d54dcc4e..e64614d3d3745773837972c4f8b646c2872e5f93 100644 (file)
@@ -63,6 +63,18 @@ struct AppIdStats
     PegCount netbios_flows;
     PegCount pop_flows;
     PegCount rsync_flows;
+    PegCount smtp_aol_clients;
+    PegCount smtp_applemail_clients;
+    PegCount smtp_eudora_clients;
+    PegCount smtp_eudora_pro_clients;
+    PegCount smtp_evolution_clients;
+    PegCount smtp_kmail_clients;
+    PegCount smtp_lotus_notes_clients;
+    PegCount smtp_microsoft_outlook_clients;
+    PegCount smtp_microsoft_outlook_express_clients;
+    PegCount smtp_microsoft_outlook_imo_clients;
+    PegCount smtp_mutt_clients;
+    PegCount smtp_thunderbird_clients;
     PegCount smtp_flows;
     PegCount smtps_flows;
     PegCount ssh_clients;
index e0aafb2248d77243cbbe0cc37e439caaac8be050..dff6228454b0ec72eb1053199e84942f4c9551c0 100644 (file)
@@ -29,6 +29,7 @@
 #include "app_info_table.h"
 #include "appid_api.h"
 #include "application_ids.h"
+#include "appid_module.h"
 
 #define  UNIT_TESTING 0
 
@@ -73,7 +74,6 @@ char* stateName [] =
 #define CLIENT_FLAG_STARTTLS_SENT   0x01
 #define CLIENT_FLAG_SMTPS           0x02
 
-#define MAX_VERSION_SIZE    64
 struct ClientSMTPData
 {
     int flags;
@@ -142,7 +142,7 @@ static const uint8_t APP_SMTP_OUTLOOK[] = "Microsoft Outlook";
 static const uint8_t APP_SMTP_OUTLOOK_EXPRESS[] = "Microsoft Outlook Express ";
 static const uint8_t APP_SMTP_IMO[] = "IMO, ";
 static const uint8_t APP_SMTP_EVOLUTION[] = "Ximian Evolution ";
-static const uint8_t APP_SMTP_LOTUSNOTES[] =  "Lotus Notes ";
+static const uint8_t APP_SMTP_LOTUS_NOTES[] =  "Lotus Notes ";
 static const uint8_t APP_SMTP_APPLEMAIL[] =  "Apple Mail (";
 static const uint8_t APP_SMTP_EUDORA[] =  "QUALCOMM Windows Eudora Version ";
 static const uint8_t APP_SMTP_EUDORAPRO[] =  "Windows Eudora Pro Version ";
@@ -156,13 +156,13 @@ static const uint8_t APP_SMTP_THUNDERBIRD_SHORT[] = "Thunderbird/";
 
 static Client_App_Pattern patterns[] =
 {
-    { (uint8_t*)HELO, sizeof(HELO)-1, 0, APP_ID_SMTP },
-    { (uint8_t*)EHLO, sizeof(EHLO)-1, 0, APP_ID_SMTP },
+    { (uint8_t*)HELO, sizeof(HELO)-1, -1, APP_ID_SMTP },
+    { (uint8_t*)EHLO, sizeof(EHLO)-1, -1, APP_ID_SMTP },
     { APP_SMTP_OUTLOOK,         sizeof(APP_SMTP_OUTLOOK)-1,        -1, APP_ID_OUTLOOK },
     { APP_SMTP_OUTLOOK_EXPRESS, sizeof(APP_SMTP_OUTLOOK_EXPRESS)-1,-1, APP_ID_OUTLOOK_EXPRESS },
     { APP_SMTP_IMO,             sizeof(APP_SMTP_IMO)-1,            -1, APP_ID_SMTP_IMO },
     { APP_SMTP_EVOLUTION,       sizeof(APP_SMTP_EVOLUTION)-1,      -1, APP_ID_EVOLUTION },
-    { APP_SMTP_LOTUSNOTES,      sizeof(APP_SMTP_LOTUSNOTES)-1,     -1, APP_ID_LOTUS_NOTES },
+    { APP_SMTP_LOTUS_NOTES,      sizeof(APP_SMTP_LOTUS_NOTES)-1,     -1, APP_ID_LOTUS_NOTES },
     { APP_SMTP_APPLEMAIL,       sizeof(APP_SMTP_APPLEMAIL)-1,      -1, APP_ID_APPLE_EMAIL },
     { APP_SMTP_EUDORA,          sizeof(APP_SMTP_EUDORA)-1,         -1, APP_ID_EUDORA },
     { APP_SMTP_EUDORAPRO,       sizeof(APP_SMTP_EUDORAPRO)-1,      -1, APP_ID_EUDORA_PRO },
@@ -234,8 +234,47 @@ static CLIENT_APP_RETCODE smtp_init(const IniClientAppAPI* const init_api, SF_LI
     return CLIENT_APP_SUCCESS;
 }
 
-static int ExtractVersion(ClientSMTPData* const fd, const uint8_t* product,
-    const uint8_t* data, AppIdSession* flowp, Packet*)
+/*
+ *    product - The product data should not include any characters
+ *              after the end of the product version (e.g. no CR, LF, etc).
+ *    prefix_len - The number of characters that are the prefix to the version,
+ *              including the NUL terminating character.
+ */
+static int extract_version_and_add_client_app(ApplicationId clientId, 
+    const int prefix_len, const uint8_t* product, const uint8_t* product_end, 
+    ClientSMTPData* const client_data, AppIdSession* flowp, 
+    AppId appId, PegCount *stat_counter)
+{
+    const u_int8_t* p;
+    u_int8_t* v;
+    u_int8_t* v_end;
+
+    v_end = client_data->version;
+    v_end += MAX_VERSION_SIZE - 1;
+
+    //  The prefix_len includes the NUL character, but product does not, so
+    //  subtract 1 from length to skip.
+    p = product + prefix_len - 1;
+    if (p >= product_end || isspace(*p))
+        return 1;
+    for (v=client_data->version; v<v_end && p < product_end; v++,p++)
+    {
+        *v = *p;
+    }
+    *v = 0;
+    smtp_client_mod.api->add_app(flowp, appId, clientId, (char*)client_data->version);
+    (*stat_counter)++;
+    return 0;
+}
+
+
+/*
+ *  Identify the product and version of the SMTP client.
+ *
+ *  Returns 0 if a recognized product is found.  Otherwise returns 1.
+ */
+static int IdentifyClientVersion(ClientSMTPData* const fd, const uint8_t* product,
+    const uint8_t* data_end, AppIdSession* flowp, Packet*)
 {
     const u_int8_t* p;
     u_int8_t* v;
@@ -246,61 +285,39 @@ static int ExtractVersion(ClientSMTPData* const fd, const uint8_t* product,
 
     v_end = fd->version;
     v_end += MAX_VERSION_SIZE - 1;
-    len = data - product;
+    len = data_end - product;
     if (len >= sizeof(MICROSOFT) && memcmp(product, MICROSOFT, sizeof(MICROSOFT)-1) == 0)
     {
         p = product + sizeof(MICROSOFT) - 1;
 
-        if (data-p >= (int)sizeof(OUTLOOK) && memcmp(p, OUTLOOK, sizeof(OUTLOOK)-1) == 0)
+        if (data_end-p >= (int)sizeof(OUTLOOK) && memcmp(p, OUTLOOK, sizeof(OUTLOOK)-1) == 0)
         {
             p += sizeof(OUTLOOK) - 1;
-            if (p >= data)
+            if (p >= data_end)
                 return 1;
             if (*p == ',')
             {
                 p++;
-                if (p >= data || *p != ' ')
+                if (p >= data_end || *p != ' ')
                     return 1;
-                p++;
-                if (p >= data || isspace(*p))
-                    return 1;
-                for (v=fd->version; v<v_end && p < data; v++,p++)
-                {
-                    *v = *p;
-                }
-                *v = 0;
-                smtp_client_mod.api->add_app(flowp, appId, APP_ID_OUTLOOK, (char*)fd->version);
-                return 0;
+                return extract_version_and_add_client_app(APP_ID_OUTLOOK, 
+                    2, p, data_end, fd, flowp, appId, 
+                    &appid_stats.smtp_microsoft_outlook_clients);
             }
             else if (*p == ' ')
             {
                 p++;
-                if (data-p >= (int)sizeof(EXPRESS) && memcmp(p, EXPRESS, sizeof(EXPRESS)-1) == 0)
+                if (data_end-p >= (int)sizeof(EXPRESS) && memcmp(p, EXPRESS, sizeof(EXPRESS)-1) == 0)
                 {
-                    p += sizeof(EXPRESS) - 1;
-                    if (p >= data || isspace(*p))
-                        return 1;
-                    for (v=fd->version; v<v_end && p < data; v++,p++)
-                    {
-                        *v = *p;
-                    }
-                    *v = 0;
-                    smtp_client_mod.api->add_app(flowp, appId, APP_ID_OUTLOOK_EXPRESS,
-                        (char*)fd->version);
-                    return 0;
+                    return extract_version_and_add_client_app(APP_ID_OUTLOOK_EXPRESS, 
+                        sizeof(EXPRESS), p, data_end, fd, flowp, appId, 
+                        &appid_stats.smtp_microsoft_outlook_express_clients);
                 }
-                else if (data-p >= (int)sizeof(IMO) && memcmp(p, IMO, sizeof(IMO)-1) == 0)
+                else if (data_end-p >= (int)sizeof(IMO) && memcmp(p, IMO, sizeof(IMO)-1) == 0)
                 {
-                    p += sizeof(IMO) - 1;
-                    if (p >= data)
-                        return 1;
-                    for (v=fd->version; v<v_end && p < data; v++,p++)
-                    {
-                        *v = *p;
-                    }
-                    *v = 0;
-                    smtp_client_mod.api->add_app(flowp, appId, APP_ID_OUTLOOK, (char*)fd->version);
-                    return 0;
+                    return extract_version_and_add_client_app(APP_ID_OUTLOOK, 
+                        sizeof(IMO), p, data_end, fd, flowp, appId, 
+                        &appid_stats.smtp_microsoft_outlook_imo_clients);
                 }
             }
         }
@@ -308,166 +325,96 @@ static int ExtractVersion(ClientSMTPData* const fd, const uint8_t* product,
     else if (len >= sizeof(APP_SMTP_EVOLUTION) && memcmp(product, APP_SMTP_EVOLUTION,
         sizeof(APP_SMTP_EVOLUTION)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_EVOLUTION) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_EVOLUTION, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_EVOLUTION, 
+            sizeof(APP_SMTP_EVOLUTION), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_evolution_clients);
     }
-    else if (len >= sizeof(APP_SMTP_LOTUSNOTES) && memcmp(product, APP_SMTP_LOTUSNOTES,
-        sizeof(APP_SMTP_LOTUSNOTES)-1) == 0)
+    else if (len >= sizeof(APP_SMTP_LOTUS_NOTES) && memcmp(product, APP_SMTP_LOTUS_NOTES,
+        sizeof(APP_SMTP_LOTUS_NOTES)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_LOTUSNOTES) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_LOTUS_NOTES, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_LOTUS_NOTES, 
+            sizeof(APP_SMTP_LOTUS_NOTES), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_lotus_notes_clients);
     }
     else if (len >= sizeof(APP_SMTP_APPLEMAIL) && memcmp(product, APP_SMTP_APPLEMAIL,
         sizeof(APP_SMTP_APPLEMAIL)-1) == 0)
     {
         p = product + sizeof(APP_SMTP_APPLEMAIL) - 1;
-        if (p >= data || *(data - 1) != ')' || *p == ')' || isspace(*p))
+        if (p >= data_end || *(data_end - 1) != ')' || *p == ')' || isspace(*p))
             return 1;
-        for (v=fd->version; v<v_end && p < data-1; v++,p++)
+        for (v=fd->version; v<v_end && p < data_end-1; v++,p++)
         {
             *v = *p;
         }
         *v = 0;
         smtp_client_mod.api->add_app(flowp, appId, APP_ID_APPLE_EMAIL, (char*)fd->version);
+        appid_stats.smtp_applemail_clients++;
         return 0;
     }
     else if (len >= sizeof(APP_SMTP_EUDORA) && memcmp(product, APP_SMTP_EUDORA,
         sizeof(APP_SMTP_EUDORA)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_EUDORA) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_EUDORA, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_EUDORA, 
+            sizeof(APP_SMTP_EUDORA), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_eudora_clients);
     }
     else if (len >= sizeof(APP_SMTP_EUDORAPRO) && memcmp(product, APP_SMTP_EUDORAPRO,
         sizeof(APP_SMTP_EUDORAPRO)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_EUDORAPRO) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_EUDORA_PRO, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_EUDORA_PRO, 
+            sizeof(APP_SMTP_EUDORAPRO), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_eudora_pro_clients);
     }
-    else if (len >= sizeof(APP_SMTP_AOL) && memcmp(product, APP_SMTP_AOL, sizeof(APP_SMTP_AOL)-
-        1) == 0)
+    else if (len >= sizeof(APP_SMTP_AOL) && memcmp(product, APP_SMTP_AOL, 
+        sizeof(APP_SMTP_AOL)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_AOL) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_AOL_EMAIL, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_AOL_EMAIL, 
+            sizeof(APP_SMTP_AOL), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_aol_clients);
     }
-    else if (len >= sizeof(APP_SMTP_MUTT) && memcmp(product, APP_SMTP_MUTT, sizeof(APP_SMTP_MUTT)-
-        1) == 0)
+    else if (len >= sizeof(APP_SMTP_MUTT) && memcmp(product, APP_SMTP_MUTT, 
+        sizeof(APP_SMTP_MUTT)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_MUTT) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_MUTT, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_MUTT, 
+            sizeof(APP_SMTP_MUTT), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_mutt_clients);
     }
     else if (len >= sizeof(APP_SMTP_KMAIL) && memcmp(product, APP_SMTP_KMAIL,
         sizeof(APP_SMTP_KMAIL)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_KMAIL) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, appId /*KMAIL_ID*/, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_KMAIL, 
+            sizeof(APP_SMTP_KMAIL), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_kmail_clients);
     }
     else if (len >= sizeof(APP_SMTP_THUNDERBIRD) && memcmp(product, APP_SMTP_THUNDERBIRD,
         sizeof(APP_SMTP_THUNDERBIRD)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_THUNDERBIRD) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_THUNDERBIRD, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_THUNDERBIRD, 
+            sizeof(APP_SMTP_THUNDERBIRD), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_thunderbird_clients);
     }
     else if (len >= sizeof(APP_SMTP_MTHUNDERBIRD) && memcmp(product, APP_SMTP_MTHUNDERBIRD,
         sizeof(APP_SMTP_MTHUNDERBIRD)-1) == 0)
     {
-        p = product + sizeof(APP_SMTP_MTHUNDERBIRD) - 1;
-        if (p >= data || isspace(*p))
-            return 1;
-        for (v=fd->version; v<v_end && p < data; v++,p++)
-        {
-            *v = *p;
-        }
-        *v = 0;
-        smtp_client_mod.api->add_app(flowp, appId, APP_ID_THUNDERBIRD, (char*)fd->version);
-        return 0;
+        return extract_version_and_add_client_app(APP_ID_THUNDERBIRD, 
+            sizeof(APP_SMTP_MTHUNDERBIRD), product, data_end, fd, flowp, appId, 
+            &appid_stats.smtp_thunderbird_clients);
     }
     else if (len >= sizeof(APP_SMTP_MOZILLA) && memcmp(product, APP_SMTP_MOZILLA,
         sizeof(APP_SMTP_MOZILLA)-1) == 0)
     {
-        for (p = product + sizeof(APP_SMTP_MOZILLA) - 1; p < data; p++)
+        for (p = product + sizeof(APP_SMTP_MOZILLA) - 1; p < data_end; p++)
         {
             if (*p == 'T')
             {
-                sublen = data - p;
+                sublen = data_end - p;
                 if (sublen >= sizeof(APP_SMTP_THUNDERBIRD_SHORT) && memcmp(p,
                     APP_SMTP_THUNDERBIRD_SHORT, sizeof(APP_SMTP_THUNDERBIRD_SHORT)-1) == 0)
                 {
-                    p = p + sizeof(APP_SMTP_THUNDERBIRD_SHORT) - 1;
-                    for (v=fd->version; v<v_end && p < data; p++)
-                    {
-                        if (*p == 0x0A || *p == 0x0D || !isprint(*p))
-                            break;
-                        *v = *p;
-                        v++;
-                    }
-                    *v = 0;
-                    smtp_client_mod.api->add_app(flowp, appId, APP_ID_THUNDERBIRD,
-                        (char*)fd->version);
-                    return 0;
+                    return extract_version_and_add_client_app(
+                        APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD_SHORT),
+                        p, data_end, fd, flowp, appId, 
+                        &appid_stats.smtp_thunderbird_clients);
                 }
             }
         }
@@ -520,7 +467,7 @@ static CLIENT_APP_RETCODE smtp_validate(const uint8_t* data, uint16_t size, cons
             {
                 /* Because we can't see any further info without decryption we settle for
                    plain APP_ID_SMTPS instead of perhaps finding data that would make calling
-                   ExtractVersion() worthwhile, So set the appid and call it good. */
+                   IdentifyClientVersion() worthwhile, So set the appid and call it good. */
                 smtp_client_mod.api->add_app(flowp, APP_ID_SMTPS, APP_ID_SMTPS, nullptr);
                 goto done;
             }
@@ -701,7 +648,7 @@ static CLIENT_APP_RETCODE smtp_validate(const uint8_t* data, uint16_t size, cons
             {
                 if (fd->headerline && fd->pos)
                 {
-                    ExtractVersion(fd, fd->headerline, fd->headerline + fd->pos, flowp, pkt);
+                    IdentifyClientVersion(fd, fd->headerline, fd->headerline + fd->pos, flowp, pkt);
                     snort_free(fd->headerline);
                     fd->headerline = nullptr;
                 }
diff --git a/src/network_inspectors/appid/client_plugins/test/CMakeLists.txt b/src/network_inspectors/appid/client_plugins/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0f73377
--- /dev/null
@@ -0,0 +1,12 @@
+set (
+    SMTP_TEST_LIBS
+    utils
+)
+
+add_library(smtp_test_depends_on_lib ../../appid_stats_counter.cc)
+
+add_cpputest(client_app_smtp_test smtp_test_depends_on_lib ${SMTP_TEST_LIBS})
+
+include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} )
+
+
diff --git a/src/network_inspectors/appid/client_plugins/test/Makefile.am b/src/network_inspectors/appid/client_plugins/test/Makefile.am
new file mode 100644 (file)
index 0000000..3af23bf
--- /dev/null
@@ -0,0 +1,15 @@
+
+AM_DEFAULT_SOURCE_EXT = .cc
+
+check_PROGRAMS = \
+client_app_smtp_test
+
+TESTS = $(check_PROGRAMS)
+
+client_app_smtp_test_CPPFLAGS = -I$(top_srcdir)/src/network_inspectors/appid @AM_CPPFLAGS@ @CPPUTEST_CPPFLAGS@
+
+client_app_smtp_test_LDADD = \
+../../appid_stats_counter.o \
+../../../../utils/libutils.a \
+@CPPUTEST_LDFLAGS@
+
diff --git a/src/network_inspectors/appid/client_plugins/test/client_app_smtp_test.cc b/src/network_inspectors/appid/client_plugins/test/client_app_smtp_test.cc
new file mode 100644 (file)
index 0000000..75f62fa
--- /dev/null
@@ -0,0 +1,245 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// client_app_smtp_test.cc author Steve Chew <stechew@cisco.com>
+// unit test for client_app_smtp
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+#include "network_inspectors/appid/client_plugins/client_app_smtp.cc"
+
+#include <string>
+
+void Debug::print(const char*, int, uint64_t, const char*, ...) { }
+
+struct AddAppData 
+{
+    AppId client_id = 0;
+    std::string *version_str = nullptr;
+} app_data;
+
+void fake_add_app(AppIdSession*, AppId, AppId client_id, const char* version)
+{
+    mock().actualCall("add_app");
+    app_data.client_id = client_id;
+    if(app_data.version_str)
+        delete app_data.version_str;
+    app_data.version_str = new std::string(version);
+}
+
+ClientAppApi fake_clientappapi = 
+{
+    nullptr,
+    nullptr,
+    &fake_add_app,
+    nullptr,
+    nullptr,
+    nullptr,
+};
+
+void check_client_version(const uint8_t *client_str, AppId client_id,
+    const char *version_str, PegCount *client_count)
+{
+       ClientSMTPData clientData;
+       const uint8_t *data_end = client_str + strlen((const char *)client_str)-2;
+       smtp_client_mod.api = &fake_clientappapi;
+
+    mock().expectOneCall("add_app");
+
+       LONGS_EQUAL(0, IdentifyClientVersion(&clientData, client_str, data_end, nullptr, nullptr));
+    LONGS_EQUAL(client_id, app_data.client_id);
+    LONGS_EQUAL(1, *client_count);
+    STRCMP_EQUAL(version_str, app_data.version_str->c_str())
+    mock().checkExpectations();
+
+}
+
+TEST_GROUP(client_app_smtp)
+{
+    void setup()
+    {
+        memset(&appid_stats, 0, sizeof(appid_stats));
+    }
+
+    void teardown()
+    {
+        delete app_data.version_str;
+        app_data.version_str = nullptr;
+        mock().clear();
+    }
+};
+
+TEST(client_app_smtp, identify_client_version_empty_data)
+{
+       ClientSMTPData clientData;
+       LONGS_EQUAL(1, IdentifyClientVersion(&clientData, nullptr, nullptr, nullptr, nullptr));
+}
+
+TEST(client_app_smtp, extract_version_and_add_client_app_success)
+{
+       ClientSMTPData clientData;
+    PegCount client_count = 0;
+    const uint8_t *client_str=(const uint8_t *)"Thunderbird 17.2\r\n";
+       const uint8_t *data_end = client_str + strlen((const char *)client_str)-2;
+       smtp_client_mod.api = &fake_clientappapi;
+
+    mock().expectOneCall("add_app");
+
+       LONGS_EQUAL(0, extract_version_and_add_client_app(APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD), client_str, data_end, &clientData, nullptr, 0, &client_count));
+    LONGS_EQUAL(1, client_count);
+    STRCMP_EQUAL("17.2", app_data.version_str->c_str())
+
+}
+
+TEST(client_app_smtp, extract_version_and_add_client_app_missing_version)
+{
+       ClientSMTPData clientData;
+    PegCount client_count = 0;
+    const uint8_t *client_str=(const uint8_t *)"Thunderbird \r\n";
+       const uint8_t *data_end = client_str + strlen((const char *)client_str)-2;
+       smtp_client_mod.api = &fake_clientappapi;
+
+       LONGS_EQUAL(1, extract_version_and_add_client_app(APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD), client_str, data_end, &clientData, nullptr, 0, &client_count));
+    LONGS_EQUAL(0, client_count);
+    mock().checkExpectations();
+
+}
+
+TEST(client_app_smtp, extract_version_and_add_client_invalid_extra_space)
+{
+       ClientSMTPData clientData;
+    PegCount client_count = 0;
+    const uint8_t *client_str=(const uint8_t *)"Thunderbird  1.0\r\n";
+       const uint8_t *data_end = client_str + strlen((const char *)client_str)-2;
+       smtp_client_mod.api = &fake_clientappapi;
+
+       LONGS_EQUAL(1, extract_version_and_add_client_app(APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD), client_str, data_end, &clientData, nullptr, 0, &client_count));
+    LONGS_EQUAL(0, client_count);
+    mock().checkExpectations();
+
+}
+
+TEST(client_app_smtp, identify_client_version_microsoft_outlook)
+{
+    const uint8_t *client_str=(const uint8_t *)"Microsoft Outlook, 15.0\r\n";
+    check_client_version(client_str, APP_ID_OUTLOOK, "15.0", &appid_stats.smtp_microsoft_outlook_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_microsoft_outlook_express)
+{
+    const uint8_t *client_str=(const uint8_t *)"Microsoft Outlook Express 13.0\r\n";
+    check_client_version(client_str, APP_ID_OUTLOOK_EXPRESS, "13.0", &appid_stats.smtp_microsoft_outlook_express_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_microsoft_outlook_imo)
+{
+    const uint8_t *client_str=(const uint8_t *)"Microsoft Outlook IMO, 11.0\r\n";
+    check_client_version(client_str, APP_ID_OUTLOOK, "11.0", &appid_stats.smtp_microsoft_outlook_imo_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_evolution)
+{
+    const uint8_t *client_str=(const uint8_t *)"Ximian Evolution 7.0\r\n";
+    check_client_version(client_str, APP_ID_EVOLUTION, "7.0", &appid_stats.smtp_evolution_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_lotus_notes)
+{
+    const uint8_t *client_str=(const uint8_t *)"Lotus Notes 2.0\r\n";
+    check_client_version(client_str, APP_ID_LOTUS_NOTES, "2.0", &appid_stats.smtp_lotus_notes_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_applemail)
+{
+    const uint8_t *client_str=(const uint8_t *)"Apple Mail (1984.1)\r\n";
+    check_client_version(client_str, APP_ID_APPLE_EMAIL, "1984.1", &appid_stats.smtp_applemail_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_applemail_missing_end_parenthesis)
+{
+    //  Missing trailing parenthesis after version.
+    const uint8_t *client_str=(const uint8_t *)"Apple Mail (1984.1\r\n";
+
+       ClientSMTPData clientData;
+       const uint8_t *data_end = client_str + strlen((const char *)client_str)-2;
+       smtp_client_mod.api = &fake_clientappapi;
+
+       LONGS_EQUAL(1, IdentifyClientVersion(&clientData, client_str, data_end, nullptr, nullptr));
+    mock().checkExpectations();
+}
+
+TEST(client_app_smtp, identify_client_version_eudora)
+{
+    const uint8_t *client_str=(const uint8_t *)"QUALCOMM Windows Eudora Version 0.3\r\n";
+    check_client_version(client_str, APP_ID_EUDORA, "0.3", &appid_stats.smtp_eudora_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_eudora_pro)
+{
+    const uint8_t *client_str=(const uint8_t *)"Windows Eudora Pro Version 2.2\r\n";
+    check_client_version(client_str, APP_ID_EUDORA_PRO, "2.2", &appid_stats.smtp_eudora_pro_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_aol)
+{
+    const uint8_t *client_str=(const uint8_t *)"AOL 6.4\r\n";
+    check_client_version(client_str, APP_ID_AOL_EMAIL, "6.4", &appid_stats.smtp_aol_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_mutt)
+{
+    const uint8_t *client_str=(const uint8_t *)"Mutt/1.5.21\r\n";
+    check_client_version(client_str, APP_ID_MUTT, "1.5.21", &appid_stats.smtp_mutt_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_kmail)
+{
+    const uint8_t *client_str=(const uint8_t *)"KMail/2112\r\n";
+    check_client_version(client_str, APP_ID_KMAIL, "2112", &appid_stats.smtp_kmail_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_mozilla)
+{
+    const uint8_t *client_str=(const uint8_t *)"Mozilla/5.0 (Windows NT 5.1; rv:8.0) Gecko/20111105 Thunderbird/21.12\r\n";
+
+    check_client_version(client_str, APP_ID_THUNDERBIRD, "21.12", &appid_stats.smtp_thunderbird_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_thunderbird)
+{
+    const uint8_t *client_str=(const uint8_t *)"Thunderbird 17.2\r\n";
+
+    check_client_version(client_str, APP_ID_THUNDERBIRD, "17.2", &appid_stats.smtp_thunderbird_clients);
+}
+
+TEST(client_app_smtp, identify_client_version_mozilla_thunderbird)
+{
+    const uint8_t *client_str=(const uint8_t *)"Mozilla Thunderbird 5.0\r\n";
+    check_client_version(client_str, APP_ID_THUNDERBIRD, "5.0", &appid_stats.smtp_thunderbird_clients);
+}
+
+//  FIXIT-M: Add additional tests for other client types (Outlook, etc).
+
+int main(int argc, char** argv)
+{
+    int return_value = CommandLineTestRunner::RunAllTests(argc, argv);
+    return return_value;
+}
+