LCOV - code coverage report
Current view: top level - src - DPM.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 110 202 54.5 %
Date: 2010-12-13 Functions: 10 19 52.6 %
Branches: 55 128 43.0 %

           Branch data     Line data    Source code
       1                 :            : // $Id: DPM.cc,v 1.1.4.14 2006/06/01 17:18:10 sommer Exp $
       2                 :            : 
       3                 :            : #include "DPM.h"
       4                 :            : #include "PIA.h"
       5                 :            : #include "Hash.h"
       6                 :            : #include "ICMP.h"
       7                 :            : #include "UDP.h"
       8                 :            : #include "TCP.h"
       9                 :            : #include "Val.h"
      10                 :            : #include "BackDoor.h"
      11                 :            : #include "InterConn.h"
      12                 :            : #include "SteppingStone.h"
      13                 :            : 
      14                 :            : 
      15                 :            : ExpectedConn::ExpectedConn(const uint32* _orig, const uint32* _resp,
      16                 :          0 :                                 uint16 _resp_p, uint16 _proto)
      17                 :            :         {
      18                 :            :         if ( orig )
      19                 :          0 :                 copy_addr(_orig, orig);
      20                 :            :         else
      21                 :            :                 {
      22                 :            :                 for ( int i = 0; i < NUM_ADDR_WORDS; ++i )
      23                 :            :                         orig[i] = 0;
      24                 :            :                 }
      25                 :            : 
      26                 :          0 :         copy_addr(_resp, resp);
      27                 :            : 
      28                 :          0 :         resp_p = _resp_p;
      29                 :          0 :         proto = _proto;
      30                 :          0 :         }
      31                 :            : 
      32                 :            : ExpectedConn::ExpectedConn(uint32 _orig, uint32 _resp,
      33                 :          0 :                                 uint16 _resp_p, uint16 _proto)
      34                 :            :         {
      35                 :            : #ifdef BROv6
      36                 :            :         // Use the IPv4-within-IPv6 convention, as this is what's
      37                 :            :         // needed when we mix uint32's (like in this construction)
      38                 :            :         // with addr_type's (for example, when looking up expected
      39                 :            :         // connections).
      40                 :            : 
      41                 :            :         orig[0] = orig[1] = orig[2] = 0;
      42                 :            :         resp[0] = resp[1] = resp[2] = 0;
      43                 :            :         orig[3] = _orig;
      44                 :            :         resp[3] = _resp;
      45                 :            : #else
      46                 :          0 :         orig[0] = _orig;
      47                 :          0 :         resp[0] = _resp;
      48                 :            : #endif
      49                 :          0 :         resp_p = _resp_p;
      50                 :          0 :         proto = _proto;
      51                 :          0 :         }
      52                 :            : 
      53                 :          0 : ExpectedConn::ExpectedConn(const ExpectedConn& c)
      54                 :            :         {
      55                 :          0 :         copy_addr(c.orig, orig);
      56                 :          0 :         copy_addr(c.resp, resp);
      57                 :          0 :         resp_p = c.resp_p;
      58                 :          0 :         proto = c.proto;
      59                 :          0 :         }
      60                 :            : 
      61                 :            : 
      62                 :          3 : DPM::DPM()
      63                 :          3 : : expected_conns_queue(AssignedAnalyzer::compare)
      64                 :            :         {
      65                 :          3 :         }
      66                 :            : 
      67                 :          1 : DPM::~DPM()
      68                 :            :         {
      69 [ +  - ][ #  # ]:          1 :         delete [] active_analyzers;
      70                 :          1 :         }
      71                 :            : 
      72                 :          3 : void DPM::PreScriptInit()
      73                 :            :         {
      74         [ +  + ]:        177 :         for ( int i = 1; i < int(AnalyzerTag::LastAnalyzer); i++ )
      75                 :            :                 {
      76                 :            :                 // Create IDs ANALYZER_*.
      77                 :            :                 ID* id = install_ID(fmt("ANALYZER_%s",
      78                 :            :                                 Analyzer::analyzer_configs[i].name),
      79                 :        174 :                                         GLOBAL_MODULE_NAME, true, false);
      80         [ -  + ]:        174 :                 assert(id);
      81                 :        174 :                 id->SetVal(new Val(i, TYPE_COUNT));
      82                 :        174 :                 id->SetType(id->ID_Val()->Type()->Ref());
      83                 :            :                 }
      84                 :          3 :         }
      85                 :            : 
      86                 :          1 : void DPM::PostScriptInit()
      87                 :            :         {
      88                 :          1 :         active_analyzers = new bool[int(AnalyzerTag::LastAnalyzer)];
      89                 :            : 
      90         [ +  + ]:         59 :         for ( int i = 1; i < int(AnalyzerTag::LastAnalyzer); i++ )
      91                 :            :                 {
      92         [ +  + ]:         58 :                 if ( ! Analyzer::analyzer_configs[i].available )
      93                 :         16 :                         continue;
      94                 :            : 
      95                 :         42 :                 active_analyzers[i] = Analyzer::analyzer_configs[i].available();
      96         [ +  + ]:         42 :                 if ( active_analyzers[i] )
      97                 :         11 :                         AddConfig(Analyzer::analyzer_configs[i]);
      98                 :            :                 }
      99                 :          1 :         }
     100                 :            : 
     101                 :         11 : void DPM::AddConfig(const Analyzer::Config& cfg)
     102                 :            :         {
     103                 :            : #ifdef USE_PERFTOOLS
     104                 :            :         HeapLeakChecker::Disabler disabler;
     105                 :            : #endif
     106                 :            : 
     107                 :         11 :         Val* index = new Val(cfg.tag, TYPE_COUNT);
     108                 :         11 :         Val* v = dpd_config->Lookup(index);
     109                 :            : 
     110                 :            : #ifdef DEBUG
     111                 :         11 :         ODesc desc;
     112                 :            : #endif
     113         [ +  + ]:         11 :         if ( v )
     114                 :            :                 {
     115                 :          5 :                 RecordVal* cfg_record = v->AsRecordVal();
     116                 :          5 :                 Val* ports = cfg_record->Lookup(0);
     117                 :            : 
     118         [ +  - ]:          5 :                 if ( ports )
     119                 :            :                         {
     120                 :          5 :                         ListVal* plist = ports->AsTableVal()->ConvertToPureList();
     121                 :            : 
     122         [ +  + ]:         18 :                         for ( int i = 0; i< plist->Length(); ++i )
     123                 :            :                                 {
     124                 :         13 :                                 PortVal* port = plist->Index(i)->AsPortVal();
     125                 :            : 
     126                 :            :                                 analyzer_map* ports =
     127         [ +  - ]:         13 :                                         port->IsTCP() ? &tcp_ports : &udp_ports;
     128                 :            : 
     129                 :            :                                 analyzer_map::iterator j =
     130                 :         13 :                                         ports->find(port->Port());
     131                 :            : 
     132         [ +  - ]:         13 :                                 if ( j == ports->end() )
     133                 :            :                                         {
     134                 :         13 :                                         tag_list* analyzers = new tag_list;
     135                 :         13 :                                         analyzers->push_back(cfg.tag);
     136                 :         13 :                                         ports->insert(analyzer_map::value_type(port->Port(), analyzers));
     137                 :            :                                         }
     138                 :            :                                 else
     139                 :          0 :                                         j->second->push_back(cfg.tag);
     140                 :            : 
     141                 :            : #ifdef DEBUG
     142                 :         13 :                                 port->Describe(&desc);
     143                 :         13 :                                 desc.SP();
     144                 :            : #endif
     145                 :            :                                 }
     146                 :            :                         }
     147                 :            :                 }
     148                 :            : 
     149                 :         11 :         DBG_LOG(DBG_DPD, "%s analyzer active on port(s) %s", cfg.name, desc.Description());
     150                 :            : 
     151                 :         11 :         Unref(index);
     152                 :         11 :         }
     153                 :            : 
     154                 :       1622 : AnalyzerTag::Tag DPM::GetExpected(int proto, const Connection* conn)
     155                 :            :         {
     156         [ +  - ]:       1622 :         if ( ! expected_conns.Length() )
     157                 :       1622 :                 return AnalyzerTag::Error;
     158                 :            : 
     159                 :            :         ExpectedConn c(conn->OrigAddr(), conn->RespAddr(),
     160                 :          0 :                         ntohs(conn->RespPort()), proto);
     161                 :            : 
     162                 :            :         // Can't use sizeof(c) due to potential alignment issues.
     163                 :            :         // FIXME: I guess this is still not portable ...
     164                 :            :         HashKey key(&c, sizeof(c.orig) + sizeof(c.resp) +
     165                 :          0 :                                 sizeof(c.resp_p) + sizeof(c.proto));
     166                 :            : 
     167                 :          0 :         AssignedAnalyzer* a = expected_conns.Lookup(&key);
     168                 :            : 
     169         [ #  # ]:          0 :         if ( ! a )
     170                 :            :                 {
     171                 :            :                 // Wildcard for originator.
     172         [ #  # ]:          0 :                 for ( int i = 0; i < NUM_ADDR_WORDS; ++i )
     173                 :          0 :                         c.orig[i] = 0;
     174                 :            : 
     175                 :            :                 HashKey key(&c, sizeof(c.orig) + sizeof(c.resp) +
     176                 :          0 :                                 sizeof(c.resp_p) + sizeof(c.proto));
     177                 :            : 
     178                 :          0 :                 a = expected_conns.Lookup(&key);
     179                 :            :                 }
     180                 :            : 
     181         [ #  # ]:          0 :         if ( ! a )
     182                 :       1622 :                 return AnalyzerTag::Error;
     183                 :            : 
     184                 :            :         // We don't delete it here.  It will be expired eventually.
     185                 :          0 :         return a->analyzer;
     186                 :            :         }
     187                 :            : 
     188                 :            : bool DPM::BuildInitialAnalyzerTree(TransportProto proto, Connection* conn,
     189                 :       1627 :                                         const u_char* data)
     190                 :            :         {
     191                 :       1627 :         TCP_Analyzer* tcp = 0;
     192                 :       1627 :         TransportLayerAnalyzer* root = 0;
     193                 :       1627 :         AnalyzerTag::Tag expected = AnalyzerTag::Error;
     194                 :       1627 :         analyzer_map* ports = 0;
     195                 :       1627 :         PIA* pia = 0;
     196                 :       1627 :         bool analyzed = false;
     197                 :            : 
     198   [ +  +  +  - ]:       1627 :         switch ( proto ) {
     199                 :            : 
     200                 :            :         case TRANSPORT_TCP:
     201                 :        938 :                 root = tcp = new TCP_Analyzer(conn);
     202                 :        938 :                 pia = new PIA_TCP(conn);
     203                 :        938 :                 expected = GetExpected(proto, conn);
     204                 :        938 :                 ports = &tcp_ports;
     205                 :        938 :                 DBG_DPD(conn, "activated TCP analyzer");
     206                 :        938 :                 break;
     207                 :            : 
     208                 :            :         case TRANSPORT_UDP:
     209                 :        684 :                 root = new UDP_Analyzer(conn);
     210                 :        684 :                 pia = new PIA_UDP(conn);
     211                 :        684 :                 expected = GetExpected(proto, conn);
     212                 :        684 :                 ports = &udp_ports;
     213                 :        684 :                 DBG_DPD(conn, "activated UDP analyzer");
     214                 :        684 :                 break;
     215                 :            : 
     216                 :            :         case TRANSPORT_ICMP: {
     217                 :          5 :                 const struct icmp* icmpp = (const struct icmp *) data;
     218   [ -  +  -  - ]:          5 :                 switch ( icmpp->icmp_type ) {
     219                 :            : 
     220                 :            :                 case ICMP_ECHO:
     221                 :            :                 case ICMP_ECHOREPLY:
     222         [ #  # ]:          0 :                         if ( ICMP_Echo_Analyzer::Available() )
     223                 :            :                                 {
     224                 :          0 :                                 root = new ICMP_Echo_Analyzer(conn);
     225                 :          0 :                                 DBG_DPD(conn, "activated ICMP Echo analyzer");
     226                 :            :                                 }
     227                 :          0 :                         break;
     228                 :            : 
     229                 :            :                 case ICMP_UNREACH:
     230         [ -  + ]:          5 :                         if ( ICMP_Unreachable_Analyzer::Available() )
     231                 :            :                                 {
     232                 :          0 :                                 root = new ICMP_Unreachable_Analyzer(conn);
     233                 :          0 :                                 DBG_DPD(conn, "activated ICMP Unreachable analyzer");
     234                 :            :                                 }
     235                 :          5 :                         break;
     236                 :            : 
     237                 :            :                 case ICMP_TIMXCEED:
     238         [ #  # ]:          0 :                         if ( ICMP_TimeExceeded_Analyzer::Available() )
     239                 :            :                                 {
     240                 :          0 :                                 root = new ICMP_TimeExceeded_Analyzer(conn);
     241                 :          0 :                                 DBG_DPD(conn, "activated ICMP Time Exceeded analyzer");
     242                 :            :                                 }
     243                 :            :                         break;
     244                 :            :                 }
     245                 :            : 
     246         [ +  - ]:          5 :                 if ( ! root )
     247                 :          5 :                         root = new ICMP_Analyzer(conn);
     248                 :            : 
     249                 :          5 :                 analyzed = true;
     250                 :          5 :                 break;
     251                 :            :                 }
     252                 :            : 
     253                 :            :         default:
     254                 :          0 :                 internal_error("unknown protocol");
     255                 :            :         }
     256                 :            : 
     257         [ -  + ]:       1627 :         if ( ! root )
     258                 :            :                 {
     259                 :          0 :                 DBG_DPD(conn, "cannot build analyzer tree");
     260                 :          0 :                 return false;
     261                 :            :                 }
     262                 :            : 
     263                 :            :         // Any scheduled analyzer?
     264         [ -  + ]:       1627 :         if ( expected != AnalyzerTag::Error )
     265                 :            :                 {
     266                 :            :                 Analyzer* analyzer =
     267                 :          0 :                         Analyzer::InstantiateAnalyzer(expected, conn);
     268                 :          0 :                 root->AddChildAnalyzer(analyzer, false);
     269                 :          0 :                 DBG_DPD_ARGS(conn, "activated %s analyzer as scheduled",
     270                 :            :                         Analyzer::GetTagName(expected));
     271                 :            : 
     272                 :            :                 // Hmm... Do we want *just* the expected analyzer, or all
     273                 :            :                 // other potential analyzers as well?  For now we only take
     274                 :            :                 // the scheduled one.
     275                 :            :                 }
     276                 :            : 
     277                 :            :         else
     278                 :            :                 { // Let's see if it's a port we know.
     279 [ +  + ][ +  - ]:       1627 :                 if ( ports && ! dpd_ignore_ports )
     280                 :            :                         {
     281                 :            :                         analyzer_map::const_iterator i =
     282                 :       1622 :                                 ports->find(ntohs(conn->RespPort()));
     283                 :            : 
     284         [ +  + ]:       1622 :                         if ( i != ports->end() )
     285                 :            :                                 {
     286                 :        693 :                                 tag_list* analyzers = i->second;
     287         [ +  + ]:       1386 :                                 for ( tag_list::const_iterator j = analyzers->begin();
     288                 :            :                                       j != analyzers->end(); j++ )
     289                 :            :                                         {
     290                 :            :                                         Analyzer* analyzer =
     291                 :        693 :                                         Analyzer::InstantiateAnalyzer(*j, conn);
     292                 :            : 
     293                 :        693 :                                         root->AddChildAnalyzer(analyzer, false);
     294                 :        693 :                                         DBG_DPD_ARGS(conn, "activated %s analyzer due to port %d", Analyzer::GetTagName(*j), conn->RespPort());
     295                 :            :                                         }
     296                 :            :                                 }
     297                 :            :                         }
     298                 :            :                 }
     299                 :            : 
     300         [ +  + ]:       1627 :         if ( tcp )
     301                 :            :                 {
     302                 :            :                 // We have to decide whether to reassamble the stream.
     303                 :            :                 // We turn it on right away if we already have an app-layer
     304                 :            :                 // analyzer, reassemble_first_packets is true, or the user
     305                 :            :                 // asks us to do so.  In all other cases, reassembly may
     306                 :            :                 // be turned on later by the TCP PIA.
     307                 :            : 
     308                 :            :                 bool reass = root->GetChildren().size() ||
     309                 :            :                                 dpd_reassemble_first_packets ||
     310                 :            :                                 tcp_content_deliver_all_orig ||
     311 [ +  + ][ -  + ]:        938 :                                 tcp_content_deliver_all_resp;
         [ #  # ][ #  # ]
     312                 :            : 
     313 [ -  + ][ #  # ]:        938 :                 if ( tcp_contents && ! reass )
                 [ -  + ]
     314                 :            :                         {
     315                 :          0 :                         PortVal dport(ntohs(conn->RespPort()), TRANSPORT_TCP);
     316                 :            :                         Val* result;
     317                 :            : 
     318         [ #  # ]:          0 :                         if ( ! reass )
     319                 :          0 :                                 reass = tcp_content_delivery_ports_orig->Lookup(&dport);
     320                 :            : 
     321         [ #  # ]:          0 :                         if ( ! reass )
     322                 :          0 :                                 reass = tcp_content_delivery_ports_resp->Lookup(&dport);
     323                 :            :                         }
     324                 :            : 
     325         [ +  - ]:        938 :                 if ( reass )
     326                 :        938 :                         tcp->EnableReassembly();
     327                 :            : 
     328                 :            :                 // Add a BackDoor analyzer if requested.  This analyzer
     329                 :            :                 // can handle both reassembled and non-reassembled input.
     330         [ -  + ]:        938 :                 if ( BackDoor_Analyzer::Available() )
     331                 :            :                         {
     332                 :          0 :                         BackDoor_Analyzer* bd = new BackDoor_Analyzer(conn);
     333                 :          0 :                         tcp->AddChildAnalyzer(bd, false);
     334                 :            :                         }
     335                 :            : 
     336                 :            :                 // Add a InterConn analyzer if requested.  This analyzer
     337                 :            :                 // can handle both reassembled and non-reassembled input.
     338         [ -  + ]:        938 :                 if ( InterConn_Analyzer::Available() )
     339                 :            :                         {
     340                 :          0 :                         InterConn_Analyzer* bd = new InterConn_Analyzer(conn);
     341                 :          0 :                         tcp->AddChildAnalyzer(bd, false);
     342                 :            :                         }
     343                 :            : 
     344                 :            :                 // Add a SteppingStone analyzer if requested.  The port
     345                 :            :                 // should really not be hardcoded here, but as it can
     346                 :            :                 // handle non-reassembled data, it doesn't really fit into
     347                 :            :                 // our general framing ...  Better would be to turn it
     348                 :            :                 // on *after* we discover we have interactive traffic.
     349                 :        938 :                 uint16 resp_port = ntohs(conn->RespPort());
     350   [ -  +  #  # ]:        938 :                 if ( SteppingStone_Analyzer::Available() &&
         [ #  # ][ #  # ]
                 [ -  + ]
     351                 :            :                      (resp_port == 22 || resp_port == 23 || resp_port == 513) )
     352                 :            :                         {
     353                 :          0 :                         AddrVal src(conn->OrigAddr());
     354         [ #  # ]:          0 :                         if ( ! stp_skip_src->Lookup(&src) )
     355                 :            :                                 {
     356                 :            :                                 SteppingStone_Analyzer* bd =
     357                 :          0 :                                         new SteppingStone_Analyzer(conn);
     358                 :          0 :                                 tcp->AddChildAnalyzer(bd, false);
     359                 :          0 :                                 }
     360                 :            :                         }
     361                 :            : 
     362                 :            :                 // Add TCPStats analyzer. This needs to see packets so
     363                 :            :                 // we cannot add it as a normal child.
     364         [ -  + ]:        938 :                 if ( TCPStats_Analyzer::Available() )
     365                 :          0 :                         tcp->AddChildPacketAnalyzer(new TCPStats_Analyzer(conn));
     366                 :            :                 }
     367                 :            : 
     368         [ +  + ]:       1627 :         if ( pia )
     369                 :       1622 :                 root->AddChildAnalyzer(pia->AsAnalyzer(), false);
     370                 :            : 
     371         [ +  + ]:       1627 :         if ( root->GetChildren().size() )
     372                 :       1622 :                 analyzed = true;
     373                 :            : 
     374                 :       1627 :         conn->SetRootAnalyzer(root, pia);
     375                 :       1627 :         root->Init();
     376                 :       1627 :         root->InitChildren();
     377                 :            : 
     378         [ -  + ]:       1627 :         if ( ! analyzed )
     379                 :          0 :                 conn->SetLifetime(non_analyzed_lifetime);
     380                 :            : 
     381         [ -  + ]:       1627 :         if ( expected != AnalyzerTag::Error  )
     382                 :            :                 conn->Event(expected_connection_seen, 0,
     383                 :          0 :                                 new Val(expected, TYPE_COUNT));
     384                 :            :         
     385                 :       1627 :         return true;
     386                 :            :         }
     387                 :            : 
     388                 :            : void DPM::ExpectConnection(addr_type orig, addr_type resp, uint16 resp_p,
     389                 :            :                         TransportProto proto, AnalyzerTag::Tag analyzer,
     390                 :          0 :                         double timeout, void* cookie)
     391                 :            :         {
     392                 :            :         // Use the chance to see if the oldest entry is already expired.
     393         [ #  # ]:          0 :         if ( expected_conns_queue.size() )
     394                 :            :                 {
     395                 :          0 :                 AssignedAnalyzer* a = expected_conns_queue.top();
     396         [ #  # ]:          0 :                 if ( a->timeout < network_time )
     397                 :            :                         {
     398         [ #  # ]:          0 :                         if ( ! a->deleted )
     399                 :            :                                 {
     400                 :            :                                 HashKey* key = new HashKey(&a->conn,
     401                 :            :                                                         sizeof(a->conn.orig) +
     402                 :            :                                                         sizeof(a->conn.resp) +
     403                 :            :                                                         sizeof(a->conn.resp_p) +
     404                 :          0 :                                                         sizeof(a->conn.proto));
     405                 :          0 :                                 expected_conns.Remove(key);
     406         [ #  # ]:          0 :                                 delete key;
     407                 :            :                                 }
     408                 :            : 
     409                 :          0 :                         expected_conns_queue.pop();
     410                 :            : 
     411                 :          0 :                         DBG_LOG(DBG_DPD, "Expired expected %s analyzer for %s",
     412                 :            :                                 Analyzer::GetTagName(analyzer),
     413                 :            :                                 fmt_conn_id(a->conn.orig, 0,
     414                 :            :                                                 a->conn.resp,
     415                 :            :                                                 a->conn.resp_p));
     416                 :            : 
     417                 :          0 :                         delete a;
     418                 :            :                         }
     419                 :            :                 }
     420                 :            : 
     421                 :          0 :         ExpectedConn c(orig, resp, resp_p, proto);
     422                 :            : 
     423                 :            :         HashKey key(&c, sizeof(c.orig) + sizeof(c.resp) +
     424                 :          0 :                         sizeof(c.resp_p) + sizeof(c.proto));
     425                 :            : 
     426                 :          0 :         AssignedAnalyzer* a = expected_conns.Lookup(&key);
     427                 :            : 
     428         [ #  # ]:          0 :         if ( a )
     429                 :          0 :                 a->deleted = true;
     430                 :            : 
     431                 :          0 :         a = new AssignedAnalyzer(c);
     432                 :            : 
     433                 :          0 :         a->analyzer = analyzer;
     434                 :          0 :         a->cookie = cookie;
     435                 :          0 :         a->timeout = network_time + timeout;
     436                 :          0 :         a->deleted = false;
     437                 :            : 
     438                 :          0 :         expected_conns.Insert(&key, a);
     439                 :          0 :         expected_conns_queue.push(a);
     440                 :          0 :         }
     441                 :            : 
     442                 :          1 : void DPM::Done()
     443                 :            :         {
     444                 :            :         // Clean up expected-connection table.
     445         [ -  + ]:          1 :         while ( expected_conns_queue.size() )
     446                 :            :                 {
     447                 :          0 :                 AssignedAnalyzer* a = expected_conns_queue.top();
     448         [ #  # ]:          0 :                 if ( ! a->deleted )
     449                 :            :                         {
     450                 :            :                         HashKey* key = new HashKey(&a->conn,
     451                 :            :                                         sizeof(a->conn.orig) +
     452                 :            :                                         sizeof(a->conn.resp) +
     453                 :            :                                         sizeof(a->conn.resp_p) +
     454                 :          0 :                                         sizeof(a->conn.proto));
     455                 :          0 :                         expected_conns.Remove(key);
     456         [ #  # ]:          0 :                         delete key;
     457                 :            :                         }
     458                 :            : 
     459                 :          0 :                 expected_conns_queue.pop();
     460                 :          0 :                 delete a;
     461                 :            :                 }
     462 [ +  - ][ +  - ]:          7 :         }
     463                 :          3 : 

Generated by: LCOV version 1.8