]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1154 in SNORT/snort3 from ftp_file_malware to master
authorHui Cao (huica) <huica@cisco.com>
Mon, 2 Apr 2018 20:51:09 +0000 (16:51 -0400)
committerHui Cao (huica) <huica@cisco.com>
Mon, 2 Apr 2018 20:51:09 +0000 (16:51 -0400)
Squashed commit of the following:

commit 4bbf6bee5c063d0747721f3af9da651814749d08
Author: Steve Chew <stechew@cisco.com>
Date:   Wed Mar 28 13:14:08 2018 -0400

    Added accessors to Stream so TcpStreamSession can be private.

commit 5efb3d5a8684fd0060062205a35c82a9c86dbde5
Author: Steve Chew <stechew@cisco.com>
Date:   Tue Mar 27 18:53:55 2018 -0400

    Added Flow::set_service and fixed FtpDataFlowData::handled_expected.

commit abd6f5bf2090b128d7fc2d1545971dcb721dca1c
Author: Steve Chew <stechew@cisco.com>
Date:   Mon Mar 26 16:30:49 2018 -0400

    Provide FLOW_SERVICE_CHANGE pub/sub event.

commit 41440f6c32995de40700f89ada68b6e6ea2b22e0
Author: Steve Chew <stechew@cisco.com>
Date:   Thu Mar 22 13:34:25 2018 -0400

    FtpDataSplitter: Added ability get TCP options length from TcpStreamSession.

commit beb656a5266ff7603a218aef177f78c5ffffb7fa
Author: Steve Chew <stechew@cisco.com>
Date:   Wed Mar 21 17:25:10 2018 -0400

    FtpDataSplitter: Base last_seg_size off of MSS.

25 files changed:
src/flow/flow.cc
src/flow/flow.h
src/framework/data_bus.h
src/network_inspectors/binder/binder.cc
src/network_inspectors/binder/dev_notes.txt
src/service_inspectors/dce_rpc/CMakeLists.txt
src/service_inspectors/dce_rpc/dce_common.h
src/service_inspectors/dce_rpc/dce_http_common.cc [deleted file]
src/service_inspectors/dce_rpc/dce_http_common.h [deleted file]
src/service_inspectors/dce_rpc/dce_http_proxy.cc
src/service_inspectors/dce_rpc/dce_http_proxy_splitter.cc
src/service_inspectors/dce_rpc/dce_http_server.cc
src/service_inspectors/dce_rpc/dce_http_server_splitter.cc
src/service_inspectors/dce_rpc/dce_tcp.cc
src/service_inspectors/dce_rpc/dce_udp.cc
src/service_inspectors/ftp_telnet/ftp_data.cc
src/service_inspectors/ftp_telnet/ftpdata_splitter.cc
src/service_inspectors/ftp_telnet/ftpdata_splitter.h
src/stream/libtcp/tcp_stream_session.cc
src/stream/libtcp/tcp_stream_session.h
src/stream/libtcp/tcp_stream_tracker.h
src/stream/stream.cc
src/stream/stream.h
src/stream/tcp/tcp_session.cc
src/utils/util_utf.cc

index b2228a287b9d270b8844e7dc4aa54d52da9d3343..9526a91e39fef82289e9fbf167b68bd9511d8f0c 100644 (file)
@@ -26,6 +26,7 @@
 #include "detection/detection_engine.h"
 #include "flow/ha.h"
 #include "flow/session.h"
+#include "framework/data_bus.h"
 #include "ips_options/ips_flowbits.h"
 #include "protocols/packet.h"
 #include "sfip/sf_ip.h"
@@ -468,3 +469,10 @@ bool Flow::is_pdu_inorder(uint8_t dir)
             && (session->missing_in_reassembled(dir) == SSN_MISSING_NONE)
             && !(ssn_state.session_flags & SSNFLAG_MIDSTREAM));
 }
+
+void Flow::set_service(Packet* pkt, const char* new_service)
+{   
+    service = new_service;
+    DataBus::publish(FLOW_SERVICE_CHANGE_EVENT, pkt);
+}   
+
index adc69f2dc80dd9a5c1ece79c44924ebf1207279e..6370a13019e74ea10e6cd87066c352e9e31b6149 100644 (file)
@@ -27,6 +27,7 @@
 // state.  Inspector state is stored in FlowData, and Flow manages a list
 // of FlowData items.
 
+#include "framework/data_bus.h"
 #include "framework/decode_data.h"
 #include "framework/inspector.h"
 #include "protocols/layer.h"
@@ -179,6 +180,7 @@ public:
     void set_ttl(Packet*, bool client);
     void set_mpls_layer_per_dir(Packet*);
     Layer get_mpls_layer_per_dir(bool);
+    void set_service(Packet* pkt, const char* new_service);
 
     uint32_t update_session_flags(uint32_t flags)
     { return ssn_state.session_flags = flags; }
index 64e109ed5cc81ae0d579d47588edbb2ac3f9b739..127f733b9d10476d61f961e52bb0fcac9bbca0fd 100644 (file)
@@ -130,5 +130,8 @@ private:
 #define THREAD_IDLE_EVENT "thread.idle"
 #define THREAD_ROTATE_EVENT "thread.rotate"
 
+// An event that indicates that the service on a flow has been updated.
+#define FLOW_SERVICE_CHANGE_EVENT "flow_service_change_event"
+
 #endif
 
index 4126965edf253ea2b147f53a671d800b7af21c7c..7afe29f8cf994280371a249a780f8d428c99ab15 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "flow/flow.h"
 #include "flow/flow_key.h"
+#include "framework/data_bus.h"
 #include "log/messages.h"
 #include "main/snort_config.h"
 #include "managers/inspector_manager.h"
@@ -596,6 +597,20 @@ private:
     vector<Binding*> bindings;
 };
 
+// When a flow's service changes, re-evaluate service to inspector mapping.
+class FlowServiceChangeHandler : public DataHandler
+{
+public:
+    FlowServiceChangeHandler() { }
+
+    void handle(DataEvent&, Flow* flow) override
+    {
+        Binder* binder = (Binder*)InspectorManager::get_binder();
+        if(binder and flow)
+            binder->exec(BinderSpace::ExecOperation::HANDLE_GADGET, flow);
+    }
+};
+
 Binder::Binder(vector<Binding*>& v)
 {
     bindings = std::move(v);
@@ -628,6 +643,9 @@ bool Binder::configure(SnortConfig* sc)
         if ( !pb->use.ips_index and !pb->use.inspection_index and !pb->use.network_index )
             set_binding(sc, pb);
     }
+
+    DataBus::subscribe(FLOW_SERVICE_CHANGE_EVENT, new FlowServiceChangeHandler);
+
     return true;
 }
 
@@ -666,6 +684,8 @@ void Binder::eval(Packet* p)
 
 int Binder::exec_handle_gadget( void* pv )
 {
+    assert(pv);
+
     Flow* flow = (Flow*)pv;
     Inspector* ins = find_gadget(flow);
 
index 4793458e48126402a0e063df1b435b1b93aa33a7..9898312fdcc0f445341c8e6ad1d2724aabd3c634 100644 (file)
@@ -26,3 +26,9 @@ The implementation is not yet optimized for performance.  A faster method
 of searching for applicable bindings should be developed.
 
 The exec() method implements specialized Inspector::Binder functionality.
+
+Binder subscribes to the FLOW_SERVICE_CHANGE_EVENT and if the event
+is received, possibly due to an inspector discovering new information
+about the flow's service, then it will call Bind::exec again to reset
+the service-to-inspector mapping.
+
index 4f7736107fcfe133956ec7bb361c054773b7c96d..1b37db8ee10ffd066572b2f1ffcfd618cfd815bb 100644 (file)
@@ -4,8 +4,6 @@ set( FILE_LIST
     dce_co.h 
     dce_common.cc 
     dce_common.h 
-    dce_http_common.cc
-    dce_http_common.h 
     dce_http_proxy.cc
     dce_http_proxy_module.cc 
     dce_http_proxy_module.h 
index a058d8568c0553a20a222c0f22a9ecf888a4b6ca..616ce004adf00eeb4ddc127efde3f192eea7ad83 100644 (file)
@@ -39,6 +39,7 @@ extern const snort::InspectApi dce_http_server_api;
 extern THREAD_LOCAL int dce2_detected;
 
 #define GID_DCE2 133
+#define DCE_RPC_SERVICE_NAME "dcerpc"
 
 enum DCE2_Policy
 {
diff --git a/src/service_inspectors/dce_rpc/dce_http_common.cc b/src/service_inspectors/dce_rpc/dce_http_common.cc
deleted file mode 100644 (file)
index c128571..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2016-2018 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.
-//--------------------------------------------------------------------------
-
-// dce_http_common.cc author Ed Borgoyn <eborgoyn@cisco.com>
-// based on work by Todd Wease
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "dce_http_common.h"
-
-#include "binder/binder.h"
-#include "managers/inspector_manager.h"
-
-void dce_http_bind(snort::Flow* flow, const char* service)
-{
-    assert (flow);
-    flow->service = service;
-
-    snort::Inspector* ins = snort::InspectorManager::get_binder();
-
-    if ( ins )
-        ins->exec(BinderSpace::ExecOperation::HANDLE_GADGET, flow);
-}
-
diff --git a/src/service_inspectors/dce_rpc/dce_http_common.h b/src/service_inspectors/dce_rpc/dce_http_common.h
deleted file mode 100644 (file)
index 5ba54cb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2016-2018 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.
-//--------------------------------------------------------------------------
-
-//dce_http_common.h author Ed Borgoyn <eborgoyn@cisco.com>
-// based on work by Todd Wease
-
-#ifndef DCE_HTTP_COMMON_H
-#define DCE_HTTP_COMMON_H
-
-#include "flow/flow.h"
-
-void dce_http_bind(snort::Flow* flow, const char* service);
-
-#endif
-
index 48adfae748bb66e82c280cb88f6d570b80fafe49..c7a875c5fd8f32864360a80554253f605bf74419 100644 (file)
 #include "config.h"
 #endif
 
-#include "dce_http_common.h"
 #include "dce_http_proxy_module.h"
-#include "dce_http_proxy_splitter.h"
 
 #include "managers/inspector_manager.h"
 #include "stream/libtcp/tcp_stream_session.h"
 
+#include "dce_http_proxy_splitter.h"
+
 using namespace snort;
 
 THREAD_LOCAL DceHttpProxyStats dce_http_proxy_stats;
@@ -53,22 +53,22 @@ void DceHttpProxy::clear(Packet* p)
 {
     Flow* flow = p->flow;
 
-    if ( flow->session != nullptr)
+    if (flow->session and flow->pkt_type == PktType::TCP)
     {
         if ( (flow->get_session_flags() & (SSNFLAG_ABORT_CLIENT | SSNFLAG_ABORT_SERVER)) == 0 )
         {
-            TcpStreamSession* session = (TcpStreamSession*)flow->session;
+            TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
 
             DceHttpProxySplitter* c2s_splitter =
-                (DceHttpProxySplitter*)(session->get_splitter(true));
+                (DceHttpProxySplitter*)(tcp_session->get_splitter(true));
 
             DceHttpProxySplitter* s2c_splitter =
-                (DceHttpProxySplitter*)(session->get_splitter(false));
+                (DceHttpProxySplitter*)(tcp_session->get_splitter(false));
 
             if ( c2s_splitter->cutover_inspector() && s2c_splitter->cutover_inspector() )
             {
                 dce_http_proxy_stats.http_proxy_sessions++;
-                dce_http_bind(flow, "dcerpc");
+                flow->set_service(p, DCE_RPC_SERVICE_NAME);
             }
             else
                 dce_http_proxy_stats.http_proxy_session_failures++;
index a72952b52fdccae528485a9154ff31502cde7409..238904200c249280277c1336c20f8c6eed5c6ee0 100644 (file)
@@ -25,7 +25,6 @@
 
 #include "dce_http_proxy_splitter.h"
 
-#include "dce_http_common.h"
 #include "dce_http_proxy_module.h"
 
 #ifdef UNIT_TEST
index e5720efb6b60b41009886c1c16890808b11c4e9e..cc5565f178de87a1c025639c046dfa78dc2ff994 100644 (file)
 #include "config.h"
 #endif
 
-#include "dce_http_common.h"
 #include "dce_http_server_module.h"
-#include "dce_http_server_splitter.h"
 
 #include "managers/inspector_manager.h"
 #include "stream/libtcp/tcp_stream_session.h"
 
+#include "dce_http_server_splitter.h"
+
 using namespace snort;
 
 THREAD_LOCAL DceHttpServerStats dce_http_server_stats;
@@ -53,19 +53,18 @@ void DceHttpServer::clear(Packet* p)
 {
     Flow* flow = p->flow;
 
-    if ( flow->session != nullptr)
+    if (flow->session and flow->pkt_type == PktType::TCP)
     {
         if ( (flow->get_session_flags() & SSNFLAG_ABORT_SERVER) == 0 )
         {
-            TcpStreamSession* session = (TcpStreamSession*)flow->session;
-
+            TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
             DceHttpServerSplitter* splitter =
-                (DceHttpServerSplitter*)(session->get_splitter(false));
+                (DceHttpServerSplitter*)(tcp_session->get_splitter(false));
 
             if ( splitter->cutover_inspector())
             {
                 dce_http_server_stats.http_server_sessions++;
-                dce_http_bind(flow, "dcerpc");
+                flow->set_service(p, DCE_RPC_SERVICE_NAME);
             }
             else
                 dce_http_server_stats.http_server_session_failures++;
index 1dd0b497568c12364bec2fe73bb310a2609d2af7..a734e8a07db04b558a0bb21c0b66e8db1f4da95e 100644 (file)
@@ -25,7 +25,6 @@
 
 #include "dce_http_server_splitter.h"
 
-#include "dce_http_common.h"
 #include "dce_http_server_module.h"
 
 #ifdef UNIT_TEST
index 441a0dcba65c15f94b93d64dcd07a836d913f3d9..a3b3de17c472733aed34b5d28d43fa1222dd6ed4 100644 (file)
@@ -28,6 +28,7 @@
 #include "detection/detection_engine.h"
 #include "utils/util.h"
 
+#include "dce_common.h"
 #include "dce_tcp_module.h"
 #include "dce_tcp_paf.h"
 
@@ -225,7 +226,7 @@ const InspectApi dce2_tcp_api =
     IT_SERVICE,
     (uint16_t)PktType::PDU,
     nullptr,  // buffers
-    "dcerpc",
+    DCE_RPC_SERVICE_NAME,
     dce2_tcp_init,
     nullptr, // pterm
     nullptr, // tinit
index 6065185c749f6238d5991c31fb3fcd3074fbde60..805ccad887fd543e6b3f79d81b0bef012e779247 100644 (file)
@@ -217,7 +217,7 @@ const InspectApi dce2_udp_api =
     IT_SERVICE,
     (uint16_t)PktType::UDP,
     nullptr,  // buffers
-    "dcerpc",
+    DCE_RPC_SERVICE_NAME,
     dce2_udp_init,
     nullptr, // pterm
     nullptr, // tinit
index 8d1659062960660d7cc801efa2e5bdc2546d5a89..6480163dcf6b3652d9babfeeb85b6b6ac66ac510 100644 (file)
@@ -206,9 +206,8 @@ FtpDataFlowData::~FtpDataFlowData()
 
 void FtpDataFlowData::handle_expected(Packet* p)
 {
-    // FIXIT-M X This is an ugly, ugly hack, but it's the way Wizard is doing it
     if (!p->flow->service)
-        p->flow->service = fd_svc_name;
+        p->flow->set_service(p, fd_svc_name);
 }
 
 void FtpDataFlowData::handle_eof(Packet* p)
index b1b6b5e1783564ecd40cb25b9c296d6a3835cd70..31d6405f260a4f47bb05061773f51d128d6b022d 100644 (file)
@@ -24,6 +24,9 @@
 #include "ftpdata_splitter.h"
 
 #include "file_api/file_flows.h"
+#include "flow/session.h"
+#include "stream/stream.h"
+
 #include "ftpp_si.h"
 
 using namespace snort;
@@ -45,10 +48,28 @@ StreamSplitter::Status FtpDataSplitter::scan(Flow* flow, const uint8_t*, uint32_
 {
     if ( len )
     {
-        if ( len != last_seg_size )
+        if(expected_seg_size == 0)
+        {
+            // FIXIT-M: Can we do better than this guess if no MSS is specified?
+            //          Malware detection won't work if expected_seg_size
+            //          doesn't match the payload lengths on packets before
+            //          the last packet.
+            expected_seg_size = 1448;
+
+            if(flow->session and flow->pkt_type == PktType::TCP)
+            {
+                expected_seg_size = Stream::get_mss(flow, to_server());
+                uint8_t tcp_options_len = Stream::get_tcp_options_len(flow, to_server());
+                if(expected_seg_size > tcp_options_len)
+                    expected_seg_size -= tcp_options_len;
+            }
+        }
+
+        if ( len != expected_seg_size )
         {
+            // Treat this as the last packet of the FTP data transfer.
             set_ftp_flush_flag(flow);
-            last_seg_size = len;
+            expected_seg_size = len;
             restart_scan();
             *fp = len;
             return FLUSH;
index 303822dc52df5f169b9b784b5554d6a9a911d16a..59c6981ce9a43aa2505b912ee11065764f93547c 100644 (file)
@@ -38,7 +38,7 @@ public:
     {
         min = sz + get_flush_bucket_size();
         restart_scan();
-        last_seg_size = 1448;  // FIXIT-H base this off mss or snaplen
+        expected_seg_size = 0;
     }
 
 
@@ -49,7 +49,7 @@ private:
     uint16_t min;
     uint16_t segs;
     uint16_t bytes;
-    uint16_t last_seg_size;
+    uint16_t expected_seg_size;
 
     void restart_scan();
 };
index a271a744e322cf58d358d8f6a5c9512e8b09fea5..7f07e60f004dc20e7e75210dca67c7c88d347ede 100644 (file)
@@ -461,6 +461,20 @@ void TcpStreamSession::set_splitter(bool to_server, StreamSplitter* ss)
     trk->set_splitter(ss);
 }
 
+uint16_t TcpStreamSession::get_mss(bool to_server) const
+{
+    TcpStreamTracker* trk = (to_server) ? client : server;
+
+    return trk->get_mss();
+}
+
+uint8_t TcpStreamSession::get_tcp_options_len(bool to_server) const
+{
+    TcpStreamTracker* trk = (to_server) ? client : server;
+
+    return trk->get_tcp_options_len();
+}
+
 StreamSplitter* TcpStreamSession::get_splitter(bool to_server)
 {
     if ( to_server )
index 8c171931fad80b48e1be5557ab6ddbcb4374bee2..9bcfd30c50d58f425c1313eb1576efb55073f414 100644 (file)
@@ -55,6 +55,8 @@ public:
     bool check_alerted(snort::Packet*, uint32_t gid, uint32_t sid) override;
     int update_alert(snort::Packet*, uint32_t /*gid*/, uint32_t /*sid*/,
         uint32_t /*event_id*/, uint32_t /*event_second*/) override;
+    uint16_t get_mss(bool to_server) const;
+    uint8_t get_tcp_options_len(bool to_server) const;
 
     static void sinit();
     static void sterm();
index 348b545c8e62fbf7cbf4b2b2cd3826185a097565..6a96840ba1f893cee003ffc26affbbc1220acbee 100644 (file)
@@ -316,6 +316,16 @@ public:
         this->mss = mss;
     }
 
+    uint8_t get_tcp_options_len() const
+    {
+        return tcp_options_len;
+    }
+
+    void set_tcp_options_len(uint8_t tcp_options_len)
+    {
+        this->tcp_options_len = tcp_options_len;
+    }
+
     void cache_mac_address(TcpSegmentDescriptor&, uint8_t direction);
     bool compare_mac_addresses(const uint8_t eth_addr[]);
 
@@ -410,8 +420,9 @@ protected:
     bool mac_addr_valid = false;
     uint32_t fin_final_seq = 0;
     bool fin_seq_set = false;  // FIXIT-M should be obviated by tcp state
-    // FIXIT-L make this protected...
+    uint8_t tcp_options_len = 0;
 
+    // FIXIT-L make this protected...
 public:
     uint16_t wscale = 0; /* window scale setting */
     uint16_t mss = 0; /* max segment size */
index 4194791140843d962a182d2fe8e90135671d9296..184896649d7992d45dee5c91f1285172ada97888 100644 (file)
@@ -41,6 +41,7 @@
 #include "utils/util.h"
 
 #include "tcp/tcp_session.h"
+#include "libtcp/tcp_stream_session.h"
 
 #ifdef UNIT_TEST
 #include "catch/snort_catch.h"
@@ -796,6 +797,23 @@ bool Stream::missed_packets(Flow* flow, uint8_t dir)
     return flow->session->are_packets_missing(dir);
 }
 
+uint16_t Stream::get_mss(Flow* flow, bool to_server)
+{
+    assert(flow and flow->session and flow->pkt_type == PktType::TCP);
+
+    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    return tcp_session->get_mss(to_server);
+}
+
+uint8_t Stream::get_tcp_options_len(Flow* flow, bool to_server)
+{
+    assert(flow and flow->session and flow->pkt_type == PktType::TCP);
+
+    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    return tcp_session->get_tcp_options_len(to_server);
+}
+
+
 #ifdef UNIT_TEST
 
 TEST_CASE("Stream API", "[stream_api][stream]")
index 03ce1dfab0e7d5e4aba52f0e658095a92bfbe964..b408d008f39afddd7e7975e1a8457c941b913991 100644 (file)
@@ -228,6 +228,10 @@ public:
     static void reg_xtra_data_log(LogExtraData, void*);
     static uint32_t get_xtra_data_map(LogFunction*&);
 
+    // TCP-specific accessor methods.
+    static uint16_t get_mss(Flow*, bool to_server);
+    static uint8_t get_tcp_options_len(Flow*, bool to_server);
+
 private:
     static void set_ip_protocol(Flow*);
 };
index 1ddc0decf3b8c522f66386af9b6213f0341d8aa8..b849e85f1cfc1c6c0f06567ffebd93167c9d1606 100644 (file)
@@ -772,6 +772,12 @@ void TcpSession::handle_data_segment(TcpSegmentDescriptor& tsd)
 
     if ( TcpStreamTracker::TCP_CLOSED != talker->get_tcp_state() )
     {
+        uint8_t tcp_options_len = tsd.get_tcph()->options_len();
+        if (tsd.is_packet_from_server())
+            server->set_tcp_options_len(tcp_options_len);
+        else
+            client->set_tcp_options_len(tcp_options_len);
+
         // FIXIT-M move this to normalizer base class, handle OS_PROXY in derived class
         if (config->policy != StreamPolicy::OS_PROXY)
         {
index 2441743dc360cecae452a121aae8d853ad385913..c3ff1d07b7a952a185aa4d02c6cf128fa3cbf740 100644 (file)
@@ -363,7 +363,10 @@ char* UtfDecodeSession::convert_character_encoding(const char* to_code, const ch
     char* out = out_buf;
     size_t iconv_rval = iconv(convert_encoding, &in_buf, &in_bytes, &out, &out_bytes);
     if (iconv_rval == (size_t)-1)
+    {
+        iconv_close(convert_encoding);
         return nullptr;
+    }
 
     *out = '\0';
     *out_buf_length = (out - out_buf);